7

I am connecting to various performance counters in the Process category. I am using the following c# method to determine the instance name to use when acquiring the counters:

private const string _categoryName = "Process";
private const string _processIdCounter = "ID Process";

public static bool TryGetInstanceName(Process process, out string instanceName)
{
    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    {
        using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
        {
            if (process.Id == (int)processIdCounter.RawValue)
            {
                instanceName = name;
                return true;
            }
        }
    }

    instanceName = null;
    return false;
}

Now, I have noticed that the instance name returned usually matches the value of Process.ProcessName.

How are the instance name and process name related?

I ask because I want to simplify the foreach loop in the routine so that I do not have to acquire the ID Process counter for instances that cannot match the current process. I envision a final method that might look like this:

public static bool TryGetInstanceName(Process process, out string instanceName)
{
    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    {
        if (name /* more or less matches */ process.ProcessName)
        {
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            {
                // ...
            }
        }
    }

    instanceName = null;
    return false;
}
shytikov
  • 9,155
  • 8
  • 56
  • 103
Xharlie
  • 2,350
  • 3
  • 19
  • 39

3 Answers3

13

Seeing that an answer was not forthcoming, I did some more trial and error testing and observed the following behaviour:

Regular Processes

It appears that, for the first regular process with a given name, the process name matches the instance name. For subsequent processes with the same name, the instance name is modified by appending #1, #2, ...

Alarmingly, it also appears to be possible for the instance name associated with the process to change. This appears to happen when processes earlier in the numeric sequence end. There is a race-condition between determining the instance name and acquiring the relevant counters!

Service Processes

Windows NT Services running under the Service Control Manager appear to behave in the same way that regular processes behave. The instance name also appears to change if you end a service-process earlier in the numeric sequence.

ASP.NET Applications

The same assumptions work for applications hosted under IIS except that the process name is w3wp. Different app. pools definitely get different processes and, by starting and stopping app. pools, I ascertained that the instance name changes in the same way, under the same circumstances as above.

Conclusion

My conclusion is that the instance name always starts with the process name and the method can be modified as follows:

public static bool TryGetInstanceName(Process process, out string instanceName)
{
    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string[] instanceNames = processCategory.GetInstanceNames();
    foreach (string name in instanceNames)
    {
        if (name.StartsWith(process.ProcessName))
        {
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            {
                if (process.Id == (int)processIdCounter.RawValue)
                {
                    instanceName = name;
                    return true;
                }
            }
        }
    }

    instanceName = null;
    return false;
}

Additionally, it is vitally important that one acknowledges the presence of the race-condition mentioned above when using the instance name returned.

(In the absence of further input, I will accept this as an answer. Feel free to correct me.)

shytikov
  • 9,155
  • 8
  • 56
  • 103
Xharlie
  • 2,350
  • 3
  • 19
  • 39
  • I was exactly looking for this :D – Kat Lim Ruiz May 11 '15 at 19:13
  • 5
    Via a [registry change](https://support.microsoft.com/en-us/help/281884/the-process-object-in-performance-monitor-can-display-process-ids-pids), you can configure windows to use the form ProcessName_PID rather than appending #1, etc, which obviously avoids the [horrible behaviour of a process' instance name changing](https://blogs.technet.microsoft.com/askperf/2010/03/29/perfmon-identifying-processes-by-pid-instead-of-instance/). – Seb Wills Jul 19 '17 at 12:29
  • It is really slow to create a *new* managed PerformanceCounter, but this basis/information can be used to make a caching-with-refresh approach with some modifications. – user2864740 Feb 27 '18 at 00:00
1

Also note that if you are monitoring muliple instances of the same process then the instance naming isn't consistent across different categories. So the solution given above only works if you are pulling counters from the same category that the pid was was pulled from. I did find the pid was in some other categores - but not all - and not with consistent naming.

mp666
  • 75
  • 1
  • 3
0

Wouldn't this solution be a little bit faster:

public static bool TryGetInstanceName(Process process, out string instanceName)
{
    PerformanceCounterCategory processCategory = new PerformanceCounterCategory(_categoryName);
    string processName = Path.GetFileNameWithoutExtension(process.ProcessName);

    string[] instanceNames = processCategory.GetInstanceNames()
            .Where(inst => inst.StartsWith(processName))
            .ToArray();
    foreach (string name in instanceNames)
    {
            using (PerformanceCounter processIdCounter = new PerformanceCounter(_categoryName, _processIdCounter, name, true))
            {
                if (process.Id == (int)processIdCounter.RawValue)
                {
                    instanceName = name;
                    return true;
                }
            }
    }

    instanceName = null;
    return false;
}

(Copied from Rick Strahl's blog and modified a bit).

Nevertheless, you need to take care: If there a multiple processes with the same name and one of them exits, the naming of all of them changes:

One thing to mention related to windows process instance names is that they change dynamically when one of the processes exits.

For example if chrome#8 exits, chrome#9 will become chrome#8 and chrome#10 >will become chrome#9. At this point getting the value of the counter >previously created for chrome#10 will throw an exception. This is really annoying if you want to to monitor multiple instances of multiple processes as it gets down to monitoring process exits and recreating all the counters (really ugly).

One way would be to change the way process instance names are generated >(see http://support.microsoft.com/kb/281884) but this has the potential of affecting other apps using the perfmon api.

shytikov
  • 9,155
  • 8
  • 56
  • 103
FranzHuber23
  • 3,311
  • 5
  • 24
  • 63
  • It has been a long time since I was working on this code but my instinct is that this solution would be _less_ efficient. Firstly, you're running the `Where` predicate on the whole array and then making another array with `ToArray()` which is simply unnecessary. In my solution, there's not need to make another array, copy stuff about in memory or to run the predicate after the needle is found in the hay-stack. – Xharlie Sep 16 '16 at 12:23
  • You might be right with the `ToArray()` but why is `Where` less efficient than just getting all instances and iterating over them? I mean the where is a optimezed kind of search via LINQ which means it's fast, isn't it? – FranzHuber23 Sep 17 '16 at 08:38
  • Oh, wait... Are we talking about speed or RAM usage right now? Because I guess my code will produce more RAM usage (with the array) but is it really less efficient in speed of the, let's say (background) process I use in my application? – FranzHuber23 Sep 17 '16 at 08:41