CPU intensive processes

How do you find the most CPU intensive processes?

If you want to know the processes that are taking most CPU on your system right now then use Get-Process

PS> Get-Process | sort CPU -Descending | select -First 10

Will display the 10 processes that are running and are using the most CPU – see the CPU( s ) field.

Couple of issues with this approach. First only running processes are shown. If a process starts and runs to completion between 2 calls to Get-Process you’ll never know it happened.  Especially relevant if the process runs over night. Second, Get-Process produces a lot of information you don’t necessarily need for this task.

The alternative is to use performance counters.

PS> Get-Counter -ListSet *Process* | select CounterSetName, Description

Lists possible counter sets you can use. The Process set looks like a good possibility:

PS> Get-Counter -ListSet Process | select -ExpandProperty Counter
\Process(*)\% Processor Time
\Process(*)\% User Time
\Process(*)\% Privileged Time
\Process(*)\Virtual Bytes Peak
\Process(*)\Virtual Bytes
\Process(*)\Page Faults/sec
\Process(*)\Working Set Peak
\Process(*)\Working Set
\Process(*)\Page File Bytes Peak
\Process(*)\Page File Bytes
\Process(*)\Private Bytes
\Process(*)\Thread Count
\Process(*)\Priority Base
\Process(*)\Elapsed Time
\Process(*)\ID Process
\Process(*)\Creating Process ID
\Process(*)\Pool Paged Bytes
\Process(*)\Pool Nonpaged Bytes
\Process(*)\Handle Count
\Process(*)\IO Read Operations/sec
\Process(*)\IO Write Operations/sec
\Process(*)\IO Data Operations/sec
\Process(*)\IO Other Operations/sec
\Process(*)\IO Read Bytes/sec
\Process(*)\IO Write Bytes/sec
\Process(*)\IO Data Bytes/sec
\Process(*)\IO Other Bytes/sec
\Process(*)\Working Set – Private

Of those \Process(*)\% Processor Time looks to be what we want.

If you just run

PS> Get-Counter -Counter ‘\Process(*)\% Processor Time’

You’ll get all counters. But you can’t give a process name so you’ll have to do a bit of digging into the object returned by Get-Counter

PS> Get-Counter -Counter ‘\Process(*)\% Processor Time’ | gm

    TypeName: Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet

Name           MemberType     Definition
—-           ———-     ———-
Equals         Method         bool Equals(System.Object obj)
GetHashCode    Method         int GetHashCode()
GetType        Method         type GetType()
ToString       Method         string ToString()
CounterSamples Property       Microsoft.PowerShell.Commands.GetCounter.PerformanceCo
Timestamp      Property       datetime Timestamp {get;set;}
Readings       ScriptProperty System.Object Readings {get=$strPaths = “”…

Timestamp is the time that you retrieved the data. Countersamples gives the data for each process:

PS> $samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
PS> $samples.Timestamp

09 July 2018 10:58:34

PS> $samples.CounterSamples | select -First 1 | Format-List

Path         : \\w510w10\process(idle)\% processor time
InstanceName : idle
CookedValue  : 803.827924975895

Just selecting the top 10 processes won’t work.

PS> $samples.CounterSamples | sort CookedValue -Descending | select -f 10 | Format-List

Path         : \\w510w10\process(_total)\% processor time
InstanceName : _total
CookedValue  : 805.382717867531

Path         : \\w510w10\process(idle)\% processor time
InstanceName : idle
CookedValue  : 803.827924975895

Path         : \\w510w10\process(powershell#1)\% processor time
InstanceName : powershell
CookedValue  : 1.55479289163616

Because you get the _total and idle counts. So you need to skip them

PS> $samples.CounterSamples | sort CookedValue -Descending | select -Skip 2 -First 10 | Format-List

By default Get-Counter takes 1 sample. If you want to  multiple samples you can use the –Continuous switch which will sample continuously. Use –SampleInterval to determine the number of seconds between samples.

Alternatively, use –MaxSamples and –SampleInterval to control the number of samples in a given period but you get a sample at each sampling interval.

However you decide to sample the data you’re going to need to preserve it for future analysis. Probably easiest is to output the top process at each sampling interval to a CSV file

$samplecount = 0

while ($true) {
   $samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
   $counter = $samples.CounterSamples | sort CookedValue -Descending | select -Skip 2 -First 1

  $props = [ordered]@{
     Time = $samples.Timestamp
     Process = $counter.InstanceName
     Value = $counter.CookedValue

  New-Object -TypeName PSObject -Property $props |
   Export-Csv -Path C:\test\counters.csv -Append -NoTypeInformation

  if ($samplecount -ge 10){break}
   Start-Sleep -Seconds 10

You may see error messages like this:

Get-Counter : The data in one of the performance counter samples is not valid. View the Status
property for each PerformanceCounterSample object to make sure it contains valid data.
At line:6 char:14
+   $samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
+              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     + CategoryInfo          : InvalidResult: (:) [Get-Counter], Exception
     + FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.GetCounterCommand

So you may not your data exactly 10 seconds apart.

You could use –ErrorAction to silently continue and suppress the messages. You’ll still get your 10 samples.

You can run the script from a job or put an outer loop that starts sampling every minute, 10 minutes or whatever you need.

If you see different process showing in your samples then your machine’s resources aren’t been hogged by a single process.

This entry was posted in Powershell. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s