Windows
banshee/windows-collection/windows/ssh/
cpu
The usage of each logical processor of a Windows, their average and the kernel time, by SSH and PowerShell
by banshee1 installation2 versions
v2Latest
Published on October 28, 2026. One naming convention across collections; documentation completed.
// The processor usage of a Windows, by SSH: each logical processor and their average, and the share of
// time spent in kernel mode (privileged time), over the interval between two runs.
// Runs one PowerShell command that only reads: the raw counters of Win32_PerfRawData_PerfOS_Processor,
// whose names are English whatever the language of Windows. The raw counters, not the formatted ones: a
// formatted percentage needs two samples taken by the same process, and each run starts a new one.
// Each counter is a time in hundreds of nanoseconds since boot; Timestamp_Sys100NS is the clock they are
// read against, so the usage over the interval is exact whatever the duration of the SSH call.
need('protocol:ssh');
per('core', 'Processors');
output('cpu-busy', 'number', array('label' => 'Overall processor usage', 'unit' => '%'));
output('privileged', 'number', array('label' => 'Kernel time', 'unit' => '%'));
output('core-busy', 'number', array('label' => 'Processor usage', 'unit' => '%', 'per' => 'core'));
output('cores', 'number', array('label' => 'Number of processors'));
watch.above('cpu-high', 'Processor saturated', 'cpu-busy', array('warning' => 85, 'critical' => 95, 'for' => '3 runs'));
watch.above('core-hot', 'A processor saturated', 'core-busy', array('warning' => 95, 'for' => '3 runs', 'enabled' => false));
watch.deviate('core-neglected', 'A processor left idle', 'core-busy', array('side' => 'below', 'warning' => 30, 'enabled' => false));
watch.expect('core-count', 'Unexpected number of processors', 'cores');
// One object per logical processor, Name "0", "1"..., and one for the whole, "_Total", left out here:
// the average is computed from the processors. PercentProcessorTime counts the idle time, despite its
// name; PercentPrivilegedTime the time in kernel mode.
$read = ssh.run("powershell -NoProfile -NonInteractive -Command \""
. "ConvertTo-Json -Compress -InputObject @(Get-CimInstance Win32_PerfRawData_PerfOS_Processor"
. " | Select-Object Name, PercentProcessorTime, PercentPrivilegedTime, Timestamp_Sys100NS)\"");
$answer = text.trim($read['output']);
if (list.count(text.match($answer, '^\[')) == 0) {
fail('misconfigured', 'PowerShell did not return the processor counters', $answer . $read['errors']);
}
$cores = 0;
$measured = 0;
$busy_sum = 0;
$privileged_sum = 0;
foreach (json.read($answer) as $row) {
if ($row['Name'] == '_Total') {
continue;
}
$name = 'cpu' . $row['Name'];
$cores = $cores + 1;
$clock = counter.rate('clock', $row['Timestamp_Sys100NS'], $name);
$idle = counter.rate('idle', $row['PercentProcessorTime'], $name);
$kernel = counter.rate('privileged', $row['PercentPrivilegedTime'], $name);
if ($clock == '' || $idle == '' || $kernel == '' || $clock <= 0) {
continue;
}
$busy = 100 * (1 - $idle / $clock);
$measured = $measured + 1;
$busy_sum = $busy_sum + $busy;
$privileged_sum = $privileged_sum + 100 * $kernel / $clock;
value('core-busy', number.round($busy, 1), $name);
}
if ($cores == 0) {
fail('misconfigured', 'No processor found in Win32_PerfRawData_PerfOS_Processor', $answer);
}
if ($measured < $cores) {
skip('First reading, or a counter went backwards: usage comes with the next run');
}
value('cpu-busy', number.round($busy_sum / $cores, 1));
value('privileged', number.round($privileged_sum / $cores, 1));
value('cores', $cores);