PowerShell Problem Solver: Process Performance Counters

Over the course of several articles, we’ve been exploring a variety of techniques and tools for getting processor and process utilization values. Last time we explored ways to find out home much of the CPU a process is consuming. I left off promising a discussion of relevant performance counters, and that’s what we are going to cover today. We’ll be using the Get-Counter cmdlet, so take a moment to read help and examples.
PowerShell Processor Article Series: 


The first step is to identify the available counters.

Available performance counters (Image Credit: Jeff Hicks)
Available performance counters (Image Credit: Jeff Hicks)
We can drill down to the Paths to see specific counters.
Available counters (Image Credit: Jeff Hicks)
Available counters (Image Credit: Jeff Hicks)
The asterisk (*) can be replaced with specific process names, which I can verify by looking at paths with instances. For the task at hand, I think the first counter in the list is what we want. Let's try it out locally.
Testing a performance counter (Image Credit: Jeff Hicks)
Testing a performance counter (Image Credit: Jeff Hicks)
Some system processes are protected so you may see an error. This counter gets all processes, but you can narrow it down to a specific instance.
Getting a specific process performance (Image Credit: Jeff Hicks)
Getting a specific process performance (Image Credit: Jeff Hicks)
For our purposes, we want all processes. Well, almost all. We don't need to see values for Idle, System and _Total, so those will need to be filtered out. And we want to get the cooked value separately.
Getting CPU intensive processes (Image Credit: Jeff Hicks)
Getting CPU intensive processes (Image Credit: Jeff Hicks)
Because I'm running PowerShell 4.0, I can take advantage of the new Where() method that performs much faster. Here's a streamlined version of the previous command.
Streamlined results (Image Credit: Jeff Hicks)
Streamlined results (Image Credit: Jeff Hicks)
The command I have been using gets a one-time counter, and I want to get an average of processor time. I can run Get-Counter and collect a number of samples.
​
This will get 12 samples, one every five seconds, which comes out to about a 1 minute sampling of all processes. Once I have the results, I will need to filter out what I don't want. Because I ultimately want averages per process, I'll go ahead and group the results on the instance name.
Grouped results (Image Credit: Jeff Hicks)
Grouped results (Image Credit: Jeff Hicks)
You'll notice that in some cases I have more than 12 instances. That is because there are multiple instances of some processes and I can't find anyway with Get-Counter to coordinate the results with a process ID. So all of the svchost processes are lumped together. The next step is to process the grouped data so that I can get the computername, the process name, and an average value.
Getting average process CPU time locally (Image Credit: Jeff Hicks)
Getting average process CPU time locally (Image Credit: Jeff Hicks)
If you wanted to use a single one-liner, you could try something like this:
​
Now that we have this working locally let's query a bunch of remote servers.
​
In this example, I am getting 60 samples total, one every five seconds. I will eventually need to break the results down by server so I'm going to group all of the counter data by the computername, which I'm extracting from the Path property using a scriptblock with Group-Object.
Grouped results (Image Credit: Jeff Hicks)
Grouped results (Image Credit: Jeff Hicks)
Now for the tricky part. I have to process the counter samples for each computer in order to get the top five by average percent processor time.
​
Because the command uses a number of pipelined expressions, the value of $_ changes. That is why in the beginning of the ForEach-Object I am defining a computername variable so that I can use it later in the output. The end result is something like this:
Process Average CPU Time (Image Credit: Jeff Hicks)
Process Average CPU Time (Image Credit: Jeff Hicks)
I could export this to a CSV file or create an HTML report. If you want something a bit easier to read, you can use Format-Table. I'll also add some formatting for the AvgCPUTime value to make it easier to read.
Formatted results (Image Credit: Jeff Hicks)
Formatted results (Image Credit: Jeff Hicks)
If you recall the original problem at the beginning of the series, there is one final step and that is to put everything together. We'll tackle that next next.