I have been tacking per-process CPU percentage and Network IO data bytes
in my recent project using System.Diagnostics.PerformanceCounter class. I felt
the online materials I found does not explain the usage of the
PerformanceCounter very well.
Some basics about PerformanceCounter.
1. A performance category has many performance counters underneath. A
performance counter may be single-instanced or contain multiple running
instances. The structure is like this:
PerformanceCategory
--
PerformanceCounter
--
Instances
To get all the built-in categories:
PerformanceCategory.GetCategories()
To get all the counters under the category that has exactly one running
instance:
PerformanceCategory.GetCounters()
2. The constructor to create a performance counter is:
Some lessons I have learned:
1. The calculated value of the first sample is always 0. E.g. the very first
time you call NextValue(), the returned value is always 0.
2. If an application has more than one running instances, even though from the
Task Manager, these running instances all have the same process name, E.g.
"TestProcess", their instance names are not the same. The instance
started first has its name as "TestProcess", the second
"TestProcess#1", the third "TestProcess#2"...the nth,
"TestProcess#n". So make sure the right instance name gets passed
into the performance counter constructor.
3. If any of the running instances of the same application exits, the instance
names will be changed. If your code doesn't update the instance name
accordingly, an "Instance not found" error will occur.
E.g. If "TestProcess" of the 2 running instances,
"TestProcess" and "TestProcess#1", stopps running,
"TestProcess#1" will be renamed to "TestProcess". If the
counter still has the name "TestProcess#1", it will throw an
exception when NextValue() is called. The instance name can be updated by
simply calling counter.InstanceName = "new instance name".
4. In my case, I am only interested in getting per-Process statistics.
Unfortunately, the PerformanceCounter does not operate against the process id.
To get around this limitation, I have created my own wrapper class called
ProcessPerformanceCounter.
public class ProcessPerformanceCounter
{
...
public
ProcessPerformanceCounter(string counterName, int processId) {...}
public string
CounterName {get{...}}
public string
InstanceName {get{...} set{...}}
public
NextValue(){...}
}
Based on the process id, I can get the process name. I can then get all running
processes under the same name. From there, I can get the correct instance name
for that particular process: