Introduction
Performance counters are a great feature of Windows which allow you to monitor applications and the whole system in a noninvasive manner. What makes them even better is a fact that you can gather the performance data remotely. You can find some more information on using Performance Counters in my previous post: Diagnosing applications using Performance Counters. When diagnosing a faulting application, I usually start with IO, Memory and CPU metrics available in the Process counter. The Process counter has a separate instance for each process that is running on your machine. By default, instance names are composed of the process name they represent and some identifier (in case there are multiple processes with the same name), eg. w3wp
, w3wp#1
, w3wp#2
. As I’m mostly working with web applications hosted in IIS, I often ran into problems which counter instance represents an application pool I want to monitor. So I wrote a simple Powershell script that will find the counter by process id:
function GetPidsFromPerfCounter([string]$ComputerName,
[string]$pcounter, [int]$ProcessId, [string]$f) {
# dump counter values
typeperf -f CSV "$ComputerName$pcounter" -sc 1 -o $f -y | Out-Null
# import and parse the CSV file
$t = import-csv $f -Delimiter ","
$t.psobject.Properties | select -skip 1 | ? {
if ($ProcessId -ge 0) { $_.value -eq $ProcessId } else { $true }
} | % { "{1,6} => {0}" -f $_.Name,$_.Value } | Sort-Object
}
function Find-ProcessCounterByPid(
[Parameter(Mandatory=$False,ValueFromPipeline=$True)][int]$ProcessId = -1,
[Parameter(Mandatory=$False,ValueFromPipeline=$False)][string]$ComputerName = $null,
[Parameter(Mandatory=$False,ValueFromPipeline=$False)][switch]$IncludeDotNet = $False
) {
if (-not [String]::IsNullOrEmpty($ComputerName) -and -not $ComputerName.StartsWith("\\")) {
$ComputerName = "\\" + $ComputerName
}
$f = [System.IO.Path]::GetTempFileName()
# native counters
GetPidsFromPerfCounter $ComputerName "\Process(*)\ID Process" $ProcessId $f
# for .net counters we need to grap CLR Memory\ID Process
if ($IncludeDotNet) {
Write-Host " ------- .NET PIDs ---------"
GetPidsFromPerfCounter $ComputerName "\.NET CLR Memory(*)\Process ID" $ProcessId $f
}
del $f
}
You might now wonder why I haven’t used the Get-Counter
cmdlet. Unfortunately, there is some bug in it and it does not print counter instance indexes (the part coming after # in the instance name) which, in our case, is a core requirement. You can paste the above code into your Powershell module and make the GetPidsFromPerfCounter private
. Then you can call Find-ProcessCounterByPid
with no arguments – in which case the cmdlet will print all available counter instances:
PS Diagnostics> Find-ProcessCounterByPid
0 => \\PECET\Process(_Total)\ID Process
0 => \\PECET\Process(Idle)\ID Process
4 => \\PECET\Process(System)\ID Process
280 => \\PECET\Process(smss)\ID Process
396 => \\PECET\Process(csrss)\ID Process
404 => \\PECET\Process(svchost#5)\ID Process
456 => \\PECET\Process(wininit)\ID Process
464 => \\PECET\Process(csrss#1)\ID Process
532 => \\PECET\Process(winlogon)\ID Process
548 => \\PECET\Process(services)\ID Process
572 => \\PECET\Process(lsass)\ID Process
576 => \\PECET\Process(nvxdsync)\ID Process
664 => \\PECET\Process(svchost)\ID Process
700 => \\PECET\Process(nvvsvc)\ID Process
724 => \\PECET\Process(nvSCPAPISvr)\ID Process
752 => \\PECET\Process(nvvsvc#1)\ID Process
768 => \\PECET\Process(svchost#1)\ID Process
796 => \\PECET\Process(SkyDrive)\ID Process
820 => \\PECET\Process(svchost#2)\ID Process
860 => \\PECET\Process(dwm)\ID Process
896 => \\PECET\Process(svchost#3)\ID Process
912 => \\PECET\Process(chrome#11)\ID Process
964 => \\PECET\Process(svchost#4)\ID Process
1008 => \\PECET\Process(vpnui)\ID Process
1148 => \\PECET\Process(vmware-authd)\ID Process
1204 => \\PECET\Process(vpnagent)\ID Process
1236 => \\PECET\Process(svchost#6)\ID Process
1244 => \\PECET\Process(googledrivesync)\ID Process
1384 => \\PECET\Process(spoolsv)\ID Process
1420 => \\PECET\Process(svchost#7)\ID Process
...
or specify a concrete PID:
PS Diagnostics> Find-ProcessCounterByPid 768
768 => \\PECET\Process(svchost#1)\ID Process
Additionally, if you are also interested in profiling .NET Applications, add -IncludeDotNet
switch that will list .NET counters instance indexes, e.g.:
PS Diagnostics> Find-ProcessCounterByPid -IncludeDotNet 5824
5824 => \\PECET\Process(powershell)\ID Process
------- .NET PIDs ---------
5824 => \\PECET\.NET CLR Memory(powershell)\Process ID
By adding -ComputerName
switch, you may also collect performance data from a remote machine. I hope you will find the script useful. If you have any ideas about how to improve it, drop me a comment.