简体   繁体   中英

Listing processes by CPU usage percentage in powershell

How does one lists the processes using CPU > 1% by piping the output from Get-Process to Where-Object ?

Complete beginner to powershell all i can think is something like this

Get-Process | Where-Object { CPU_Usage -gt 1% }

If you want CPU percentage, you can use Get-Counter to get the performance counter and Get-Counter can be run for all processes. So, to list processes that use greater than say 5% of CPU use:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.CookedValue -gt 5}

This will list the processes that was using >5% of CPU at the instance the sample was taken. Hope this helps!

There are several points to note here:

  • first, you have to use the $_ variable to refer to the object currently coming from the pipe.
  • second, Powershell does not use % to express percentage -- instead, % represents the modulus operator. So, when ou want percentage, you have to transform your number by yourself by simply multiplying it by 0.01 .

  • third, the Get-Process cmdlet does not have a field CPU_Usage ; a summary on its output can be found here . About the field CPU is says: "The amount of processor time that the process has used on all processors, in seconds." So be clear on what you can expect from the numbers.

Summarizing the command can be written as

Get-Process | Where-Object { $_.CPU -gt 100 }

This gives you the processes which have used more than 100 seconds of CPU time.

If you want something like a relative statement, you first have to sum up all used times, and later divide the actual times by the total time. You can get the total CPU time eg by

Get-Process | Select-Object -expand CPU | Measure-Object -Sum | Select-Object -expand Sum

Try to stack it together with the previous command.

Further improving earlier answers by adding dynamic detection of the number of logic cores so the percentage can be adjusted back to what us common mortals expect to see, where 100% means all of the CPU bandwidth of the machine, and there is no value greater than 100%. Includes a filter set at 10%, which one can adjust as appropriate. The assumption is that people will be interested in finding processes with high overload processor usage and not want to list the numerous idle processes of the machine.

$NumberOfLogicalProcessors=(Get-WmiObject -class Win32_processor | Measure-Object -Sum NumberOfLogicalProcessors).Sum
(Get-Counter '\Process(*)\% Processor Time').Countersamples | Where cookedvalue -gt ($NumberOfLogicalProcessors*10) | Sort cookedvalue -Desc | ft -a instancename, @{Name='CPU %';Expr={[Math]::Round($_.CookedValue / $NumberOfLogicalProcessors)}}

Sample output:

InstanceName CPU %
------------ -----
_total         100
idle           100

I was looking for a solution to get cpu, mem utilization by process. All solutions I tried where I would get the cpu but those numbers were not matching with taskmanager. So I wrote my own. Following will provide accurate cpu utilization by each process. I tested this on a I7 laptop.

$Cores = (Get-WmiObject -class win32_processor -Property numberOfCores).numberOfCores;
 $LogicalProcessors = (Get-WmiObject –class Win32_processor -Property NumberOfLogicalProcessors).NumberOfLogicalProcessors;
 $TotalMemory = (get-ciminstance -class "cim_physicalmemory" | % {$_.Capacity})



$DATA=get-process -IncludeUserName | select @{Name="Time"; Expression={(get-date(get-date).ToUniversalTime() -uformat "%s")}},`
  ID,  StartTime,  Handles,WorkingSet, PeakPagedMemorySize,  PrivateMemorySize, VirtualMemorySize,`
   @{Name="Total_RAM"; Expression={ ($TotalMemory )}},`
   CPU,
    @{Name='CPU_Usage'; Expression = { $TotalSec = (New-TimeSpan -Start $_.StartTime).TotalSeconds
            [Math]::Round( ($_.CPU * 100 / $TotalSec) /$LogicalProcessors, 2) }},`
  @{Name="Cores"; Expression={ ($Cores )}},`
  @{Name="Logical_Cores"; Expression={ ($LogicalProcessors )}},`

 UserName, ProcessName, Path |  ConvertTo-Csv 

In addition to Get-Counter , you can also use Get-WmiObect to list and filter processes.

powershell "gwmi Win32_PerfFormattedData_PerfProc_Process -filter 'PercentProcessorTime > 1' | Sort PercentProcessorTime -desc | ft Name, PercentProcessorTime"

Alternatively, for Get-Counter , here is an example showing number format conversion to get rid of the annoying decimal places of the CookedValue . In addition to filtering, this example also illustrates sorting, limiting columns, and output formatting:

powershell "(Get-Counter '\Process(*)\% Processor Time').Countersamples | Where cookedvalue -gt 3 | Sort cookedvalue -Desc | ft -a instancename, @{Name='CPU %';Expr={[Math]::Round($_.CookedValue)}}"

Get-Process is not the right cmdlet as it doesn't provide instantaneous CPU utilization.

You can also get the total load for all processors:

powershell "gwmi win32_processor | Measure-Object LoadPercentage -Average | ft -h Average"

Or,

typeperf -sc 4 "\Processor(_Total)\% Processor Time"

I'd like to Add my version of code just to assist anyone that is having trouble with the same issues. I needed one that gave me the CPU and Memory and this my take on the subject. It can export over time if you setup a scheduled task in windows, and it appends to a CSV file output and you can get multiple processes.

    $Cores = (Get-WmiObject -class win32_processor -Property numberOfCores).numberOfCores;
    $LogicalProcessors = (Get-WmiObject –class Win32_processor -Property NumberOfLogicalProcessors).NumberOfLogicalProcessors;
    $TotalMemory = (get-ciminstance -class "cim_physicalmemory" | % {$_.Capacity})
    #EDIT the PATH for the CSV output to be located
    $PATH = "C:\temp"
    #You can add more processes here as variables or just CommaSeperate them like below
    $Process1 = 'explorer'
    $Process2 = 'chrome'

    Get-Process -Name $Process1,$Process2 | select @{N="TimeStamp";E={Get-Date -Format 'dd/MM/yyyy HH:mm:ss.fff'}},
      ID,  
      name,
      Handles,
      PeakPagedMemorySize,
      PrivateMemorySize,
      VirtualMemorySize,
      @{Name="Memory_MB"; Expression = {[Math]::Round(($_.workingSet / 1mb),2)}},
      @{Name='CPU_Usage'; Expression = { $TotalSec = (New-TimeSpan -Start $_.StartTime).TotalSeconds [Math]::Round( ($_.CPU * 100 / $TotalSec) /$LogicalProcessors, 2) }},
      @{Name="Cores"; Expression={ ($Cores )}},
      @{Name="Logical_Cores"; Expression={ ($LogicalProcessors )}},
      Path | Export-Csv -Path "$PATH\CPU_MEMORY_$env:COMPUTERNAME.csv" -NoTypeInformation -Append

I know this is above the OP's question and goes a bit further but I hope this helps!

How about this for one process?

$sleepseconds = 1
$numcores = 4
$id = 5244

while($true) {
  $cpu1 = (get-process -Id $id).cpu
  sleep $sleepseconds
  $cpu2 = (get-process -Id $id).cpu
  [int](($cpu2 - $cpu1)/($numcores*$sleepseconds) * 100)
}
$Result = Invoke-Command -ComputerName $ServerName -ScriptBlock {
    Get-Counter "\Process(*)\% Processor Time" -ErrorAction SilentlyContinue `
    | Select-Object -ExpandProperty CounterSamples `
    | Where-Object {$_.Status -eq 0 -and $_.instancename -notin "_total", "idle", "" -and $_.CookedValue/$env:NUMBER_OF_PROCESSORS -gt 0} `
    | Sort-Object CookedValue -Descending `
        | Select-Object @{N="ServerName";E={$env:COMPUTERNAME}},
        @{N="ProcessName";E={
            $friendlyName = $_.InstanceName
            try {
                $procId = [System.Diagnostics.Process]::GetProcessesByName($_.InstanceName)[0].Id
                $proc = Get-WmiObject -Query "SELECT ProcessId, ExecutablePath FROM Win32_Process WHERE ProcessId =$procId"
                $procPath = ($proc | where { $_.ExecutablePath } | select -First 1).ExecutablePath
                $friendlyName = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($procPath).FileDescription
            } catch { }
            $friendlyName
        }},
        @{N="CPU_Percent";E={[System.Math]::Round(($_.CookedValue/$env:NUMBER_OF_PROCESSORS), 2)}},
        @{N="TimeStamp";E={Get-Date -Format 'dd/MM/yyyy HH:mm:ss.fff'}} -First 10 `
 $Result
} 

# If you want to export result to your SQL Server table you can use DbaTools Powershell Modules.
$Result | Select -Property ServerName, ProcessName, CPU_Percent, TimeStamp | ConvertTo-DbaDataTable | Write-DbaDbTableData -SqlInstance $OutInstance -Database $OutDB -Schema dbo -Table WindowsTopCPUProcesses

To invoke vs. multible servers in parallel, and return process, cpu usage, servername, and corecount the following works well.

Mind get-count takes 1 value, if you want to improve it use the MaxSamples switch, and average it over say 5sec (should be easy to impliment in the following script).

$allServers = "server1", "PC001", "server2"

$CPUUsage = Invoke-Command -ComputerName $allServers -ScriptBlock {
    #Unhash the numbers at the end if you want to take the top 10, according to cpu usage (take say 2..6 for top 5 minus idle/total depending on host OS)
    $counters = ((Get-Counter "\Process(*)\% Processor Time").CounterSamples | Sort-Object -Property CookedValue -Descending)#[0..9]
    [int]$coreCount = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors
    $counters | ForEach-Object {
        Add-Member -MemberType NoteProperty -Name "CPU_PCT" -Value ([math]::Round($_.CookedValue / $coreCount))[0] -InputObject $_
        Add-Member -MemberType NoteProperty -Name "Host" -Value ($_.Path -replace "^\\\\|\\process.*$","") -InputObject $_
        Add-Member -MemberType NoteProperty -Name "CoreCount" -Value $coreCount -InputObject $_
    }
    $counters | Select-Object InstanceName,CPU_PCT,Host,Path,CoreCount
}

#Sort the grid view with the build in search functions
$CPUUsage | Sort-Object -Property Host,CPU_PCT -Descending | Select-Object -Property Host,InstanceName,CPU_PCT,CoreCount,Path | Out-GridView -Title "CPU Usage"

Would be great if this script would work with a process name.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM