From b419507a29e61dcb9b9b34efc1100f7070dd36f6 Mon Sep 17 00:00:00 2001 From: "Andon M. Coleman" Date: Sun, 3 Sep 2023 06:20:25 -0400 Subject: [PATCH] Fix incorrect CPU utilization statistics on Windows 11 22H2 and newer --- IntelPresentMon/ControlLib/WmiCpu.cpp | 25 ++++++++++++++++++------- IntelPresentMon/ControlLib/WmiCpu.h | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/IntelPresentMon/ControlLib/WmiCpu.cpp b/IntelPresentMon/ControlLib/WmiCpu.cpp index 1dd7b4da..84a211fe 100644 --- a/IntelPresentMon/ControlLib/WmiCpu.cpp +++ b/IntelPresentMon/ControlLib/WmiCpu.cpp @@ -8,7 +8,7 @@ std::wstring kProcessorFrequency = L"\\Processor Information(_Total)\\Processor Frequency"; std::wstring kProcessorPerformance = L"\\Processor Information(_Total)\\% Processor Performance"; -std::wstring kProcessorTime = L"\\Processor(_Total)\\% Processor Time"; +std::wstring kProcessorIdleTime = L"\\Processor(_Total)\\% Idle Time"; WmiCpu::WmiCpu() { HQUERY temp_query = nullptr; @@ -32,11 +32,11 @@ WmiCpu::WmiCpu() { "PdhAddCounter failed when adding processor performance counter"}; } - if (const auto result = PdhAddCounter(query_.get(), kProcessorTime.c_str(), 0, - &processor_time_counter_); + if (const auto result = PdhAddCounter(query_.get(), kProcessorIdleTime.c_str(), 0, + &processor_idle_time_counter_); result != ERROR_SUCCESS) { throw std::runtime_error{ - "PdhAddCounter failed when adding processor time counter"}; + "PdhAddCounter failed when adding processor idle time counter"}; } // Most counters require two sample values to display a formatted value. @@ -105,13 +105,24 @@ bool WmiCpu::Sample() noexcept { } } - // Sample cpu utilization + // Sample cpu idle time, and compute cpu utilization using it (Windows 11 Fix) + // + // Beginning with Windows 11 22H2, the performance counters for CPU idle time + // in SystemProcessorPerformanceInformation are broken and statistics derived + // from those counters will always indicate single-digit cpu utilization %. + // + // Idle time reported in SystemProcessorIdleInformation is still consistent on + // all versions of Windows, as well as WMI provisioned "Processor\% Idle Time". + // + // To measure CPU utilization accurately on all systems, it must be calculated: + // + // 100.0 - "Processor(_Total)\% Idle Time" { if (const auto result = - PdhGetFormattedCounterValue(processor_time_counter_, PDH_FMT_DOUBLE, + PdhGetFormattedCounterValue(processor_idle_time_counter_, PDH_FMT_DOUBLE, &counter_type, &counter_value); result == ERROR_SUCCESS) { - info.cpu_utilization = counter_value.doubleValue; + info.cpu_utilization = 100.0 - counter_value.doubleValue; SetTelemetryCapBit(CpuTelemetryCapBits::cpu_utilization); } } diff --git a/IntelPresentMon/ControlLib/WmiCpu.h b/IntelPresentMon/ControlLib/WmiCpu.h index 5e79f884..91d5ee9d 100644 --- a/IntelPresentMon/ControlLib/WmiCpu.h +++ b/IntelPresentMon/ControlLib/WmiCpu.h @@ -30,7 +30,7 @@ class WmiCpu : public CpuTelemetry { std::unique_ptr, PDHQueryDeleter> query_; HCOUNTER processor_frequency_counter_; HCOUNTER processor_performance_counter_; - HCOUNTER processor_time_counter_; + HCOUNTER processor_idle_time_counter_; LARGE_INTEGER next_sample_qpc_ = {}; LARGE_INTEGER frequency_ = {}; std::string cpu_name_;