During my last session with a customer, I faced an interesting and challenging issue in the Windows environment. The question was: “How can I monitor the list of the most CPU-demanding processes?” The request immediately looked like one that’s very simple to understand, but hard to implement.
We look for something out of the box from the community, but nothing provided us with a proper solution. We found something near our target in this repository: https://github.com/Linuxfabrik/monitoring-plugins. A python script (cpu-usage) is provided there, but in order to use it in a Windows environment, it requires either installing Python or creating an executable from the Python code using for example PyInstaller. (The Python installation is not always feasible and the executable is not very performant.) And although this script provides the necessary information about the CPU, it gives minimal information about process time usage.
.\cpu-usage.exe
1.9% - system: 1.9%
guest: 0.0%, guest_nice: 0.0%, iowait: 0.0%, irq: 0.0%, nice: 0.0%, softirq: 0.0%, steal: 0.0%, user: 0.0%
ctx_switches: 104.0M, interrupts: 36.6M, soft_interrupts: 0.0
Top3 processes using the most cpu time:
1. WmiPrvSE.exe: 12m 14s
2. MsMpEng.exe: 4m 52s
3. chrome.exe: 2m 593ms|'cpu-usage'=1.9%;80;90;0;100 'system'=1.9%;;;0;100 'guest'=0%;;;0;100 'guest_nice'=0%;;;0;100 'iowait'=0%;;;0;100 'irq'=0%;;;0;100 'nice'=0%;;;0;100 'softirq'=0%;;;0;100 'steal'=0%;;;0;100 'user'=0.0%;;;0;100 'ctx_switches'=104001412c;;;0; 'interrupts'=36624025c;;;0; 'soft_interrupts'=0c;; ;0;
After some testing, we decided to implement a customized check that provide the proper information by ourselves. Powershell provides this cmdlet: Get-Counter
that gets performance counter data directly from the performance monitoring instrumentation in the Windows family of operating systems. Filtering the result by \Process(*)\% Processor Time
we obtain the percentage of time that a process is in use. The CPU utilization is usually computed by:
CPU Utilization = (Total Time Spent on Non-Idle Tasks / Total Time) x 100
This cmdlet simplifies the data, and we can then manage that to obtain the desired output:
# By default check top 5 processes
if (-not $number) { $number = 5 }
$PROCESSES = @{};
$DICT = @{};
#'it-IT'
# $PROCESSES=(Get-Counter "\Processo(*)\% Tempo Processo" -ErrorAction SilentlyContinue 2>$null).CounterSamples;
$PROCESSES = (Get-Counter "\Process(*)\% Processor Time" -ErrorAction SilentlyContinue 2>$null).CounterSamples;
for ($i = 0; $i -lt $PROCESSES.Count; $i++) {
if ($PROCESSES[$i].InstanceName -ne "idle" -And $PROCESSES[$i].InstanceName -ne "_total") {
if ($($PROCESSES[$i].CookedValue) -gt 0) {
$DICT[$PROCESSES[$i].InstanceName] += $($PROCESSES[$i].CookedValue);
}
}
};
$TOPPROCESSES = ($DICT.GetEnumerator() | Sort-Object { $_.Value } -Descending) | Select-Object -First $number;
Write-Output "[OK] Top $(if ($number -lt $DICT.Count) {Write-Output $number} else { Write-Output $($DICT.Count-1)}) process(es):";
foreach ($item in $TOPPROCESSES) {
Write-Host $item.Name " " $([math]::Round($item.Value, 2))"%";
}
Write-Host -NoNewline " | ";
for ($i = 0; $i -lt $TOPPROCESSES.Count; $i++) {
Write-Host -NoNewline "$($TOPPROCESSES[$i].Name -replace "\s","_")=$($TOPPROCESSES[$i].Value);;;0; "
}
The output provided by the script has just the needed info:
./check_top_cpu_processes.ps1 -n 4
[OK] Top 4 process(es):
audiodg 3.11 %
dwm 1.56 %
powershell_ise 1.56 %
vmware-vmx 1.56 %
| audiodg=3.1130093107369;;;0; dwm=1.55650465536845;;;0;
powershell_ise=1.55650465536845;;;0; vmware-vmx=1.55650465536845;;;0;
This script doesn’t need any additional packages, can be run as a common PowerShell script, and can be easily customized.
Did you find this article interesting? Does it match your skill set? Our customers often present us with problems that need customized solutions. In fact, we’re currently hiring for roles just like this and others here at Würth Phoenix.