31

I am tracking multiple instances of the same application and need to get the memory and cpu use of both processes. However, I cant seem to figure out a way to use the performance counter and know which result is for which process. I have seen that I can append #1 and such to the end of the name to get results for each, but that doesn't tell me which one is for which process.

How can I determine the ProcessId or pass the process ID to the counter to get the result per each process with same name?

PerformanceCounterCPU.CategoryName = "Process";
PerformanceCounterCPU.CounterName = "% Processor Time";
PerformanceCounterCPU.InstanceName = proc.ProcessHandle.ProcessName;

PerformanceCounterMemory.CategoryName = "Process";
PerformanceCounterMemory.CounterName = "Working Set - Private";
PerformanceCounterMemory.InstanceName = proc.ProcessHandle.ProcessName;
JeremyK
  • 1,075
  • 1
  • 22
  • 45
  • 1
    Wow! I have faced **exactly** the same issue 2 years ago, and I didn't found any answer at that time... Waiting too for the answer now ;) – ken2k Feb 02 '12 at 16:06
  • Thanks Ben. unfortunately the problems I have asked questions on in the past have been rare and were not solved. I ended up backing away from using the methods that were in those questions. – JeremyK Feb 02 '12 at 16:07
  • Ken, The answer below did the trick – JeremyK Feb 02 '12 at 16:14
  • Yep it does, but isn't really straightforward IMHO. This actually should work, but I expected something simpler and more robust. And as far as I can remember, there might be some trouble with different executables that have the same name (don't really remember actually). – ken2k Feb 02 '12 at 16:25
  • @ken2k the linked question has an answer that points to a MS KB entry with a simpler approach, but it involves setting registry values. So far as my searching and experimenting on this matter goes this is the best solution I can find for default machine configuration. Here's the kb: http://support.microsoft.com/kb/281884 – TheXenocide Sep 21 '12 at 18:29
  • Here's a blog post that describes this scenario in some detail including some reusable code. http://weblog.west-wind.com/posts/2014/Sep/27/Capturing-Performance-Counter-Data-for-a-Process-by-Process-Id – Rick Strahl Sep 27 '14 at 20:45
  • 1
    I love when I go back and see one of my old questions and wonder, why in the world did I need this information... Then it all comes back to me. the joys of programming. – JeremyK Mar 25 '16 at 14:32

2 Answers2

34

This answer to a related question might work:

private static string GetProcessInstanceName(int pid)
{
  PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");

  string[] instances = cat.GetInstanceNames();
  foreach (string instance in instances)
  {

     using (PerformanceCounter cnt = new PerformanceCounter("Process",  
          "ID Process", instance, true))
     {
        int val = (int) cnt.RawValue;
        if (val == pid)
        {
           return instance;
        }
     }
  }
  throw new Exception("Could not find performance counter " + 
      "instance name for current process. This is truly strange ...");
}
Community
  • 1
  • 1
M.Babcock
  • 18,753
  • 6
  • 54
  • 84
  • Perfect. I tried using the raw value in the past, however it never seemed to match the pid. This has worked, thank you very much. – JeremyK Feb 02 '12 at 16:13
  • 8
    For the sake of providing credit, this originally comes from an old Ingo Rammer blog (who's articles taught me a lot in my early .NET days): http://weblogs.thinktecture.com/ingo/2004/06/getting-the-current-process-your-own-cpu-usage.html – TheXenocide Sep 21 '12 at 18:30
  • 2
    Good to know. I credited the source I copied it from, that's enough for me. – M.Babcock Sep 21 '12 at 21:29
  • 3
    One has to wonder how a programming API managed to end up using names instead of identifiers for, well, identifying a process. This answer works for the corner case in which `Process.GetCurrentProcess().ProcessName` returns a completely useless value if multiple instances are running. – Roman Starkov Jun 13 '15 at 23:12
  • 1
    Only one problem with this solution - "Memory traffic". So if you will call this method many very frequently in your code it will generate a lot off memory (byte arrays in internals of PerfomanceCounter class). – Rail Jan 13 '16 at 12:18
  • 1
    It's also very CPU-expensive. – Xcalibur37 Jan 19 '16 at 23:36
  • Another problem, not sure how to solve it: the instance name corresponding to a process can change at any time (like if another process with the same name goes away) – RobSiklos Jun 23 '17 at 21:02
  • 1
    Also, you can seriously optimize this solution by only creating an "ID Process" `PerformanceCounter` for instances which start with the name of the process. – RobSiklos Jun 23 '17 at 21:07
  • string[] instances = cat.GetInstanceNames() gets all the processes, it would be better to filter only processes with the same name you are looking for to optimize a little – atikot Mar 20 '18 at 15:40
  • 1
    Does this really answer the question? I need to find a way to get a PerformanceCounter per ProcessID, not per name. A process name like w3wp (IIS) can repeat even if multiple processes are used(f.e. different application-pools). This method only returns `w3wp` which is not unique. So this doesn't answer how to get the `PerformanceCounter` by ID instead of (non-unique)names. Your method could be reduced to `return Process.GetProcessById(pid)?.ProcessName` – Tim Schmelter Apr 27 '18 at 10:47
  • @TimSchmelter, yes it does answer it. If I have the same app called "App" running multiple times I get instance names of App, App #1, App #2, etc. This method gives me, for a given process ID, the name I can use to make sure I get the correct Windows-generated name for the process in perfmon. It could be App #2 this time and App #37 another day. I can then use this unique name (only persisting for the life of the process) to query into some other counters, like .NET's number of queued threads, for example. Tediously though, the names change to fill the holes when you close processes :( – Ian Yates Mar 09 '21 at 04:34
5

If you don't mind a machine-wide registry change, you can configure Windows to use the form ProcessName_ProcessID for Perf Counter instance names, rather than appending #1, #2, etc:

Create DWORD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfProc\Performance\ProcessNameFormat and set its value to 2.

If you do stick with the #1, #2 etc form, beware that the instance name for a given process can change during the process' lifetime!

Seb Wills
  • 834
  • 9
  • 11
  • That works for Process counters ... process identifiers in Perfmon become xxx_1570, for example. BUT it doesn't work for other counters, like .NET Memory counters, which still associate with the _#1, _#2 style names. That's either a bug or there is some other registry value that needs to be set. – Dave Hein Apr 05 '22 at 22:49