Performance Counters are a great feature of Windows which allow you to monitor applications and the whole system in a noninvasive manner. Which 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 problem 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, eg.:
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 how to improve it drop me a comment 🙂