Recently I had a chance to realize that PerfMon "\\Process\\% Processor Time" for the single process, is actually different counter than "CPU usage", displayed in the Task Manager. Performance Counters are accessed through *PerfMon API*, header file Pdh.h:
https://docs.microsoft.com/en-us/windows/win32/api/pdh/
whereas there's no direct way to get TaskManager's CPU Usage.
Here’s a thread from Microsoft TechNet, discussing differences, unfortunately does not solve my problem:
https://social.technet.microsoft.com/Forums/en-US/0435e7c5-3cda-41a0-953e-7fa462fde03b/perfmon-process-processor-time-vs-task-manager8217s-cpu-usage-for-monitoring-a-specific?forum=perfmon
Problem emerged when I realized, how huge discrepancies between values they provide.
In my application I need to measure % CPU usage, exactly or at least very close to values, provided by the Task Manager. However, values provided by PerfMon API could be 20-30% lower, why even discrepancies are different and not proportional, is an another question. For example, if tested process payload 50-55% according to Task Manager, PerfMon API reports 35-40%
Here’s my class, implemented for calculating total process CPU usage using PerfMon API (return codes check and insignificant parts of the code have been skipped)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
// PdhCPUCounter.h
class PdhCPUCounter {
public:
// @param counter_name: "\\Process(*ProcessName*)\\% Processor Time"
explicit PdhCPUCounter(const std::string& counter_name);
// Release handles here
virtual ~PdhCPUCounter();
// Provide actual CPU usage value in range [0.0, 1.0]
double getCPUUtilization() const;
private:
// Low-level query
PDH_FMT_COUNTERVALUE getFormattedCounterValue() const;
// Needed for calculation
size_t m_threads;
// Counter format: "\\Process(*ProcessName*)\\% Processor Time"
std::string m_counter_name;
// CPU counter handle
PDH_HCOUNTER m_counter = INVALID_HANDLE_VALUE;
// Query to PerfMon handle
PDH_HQUERY m_query = INVALID_HANDLE_VALUE;
};
// PdhCPUCounter.cpp
PdhCPUCounter::PdhCPUCounter(const std::string& counter_name) :
m_counter_name(counter_name),
m_threads(std::thread::hardware_concurrency())
{
PdhOpenQuery(nullptr, 0, &m_query);
PdhAddEnglishCounter(m_query, m_counter_name.c_str(), 0, &m_counter);
PdhCollectQueryData(m_query);
}
PDH_FMT_COUNTERVALUE PdhCPUCounter::getFormattedCounterValue() const
{
PdhCollectQueryData(m_query);
PDH_FMT_COUNTERVALUE val;
PdhGetFormattedCounterValue(m_counter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, nullptr, &val);
return val;
}
double PdhCPUCounter::getCPUUtilization() const
{
const auto &val = getFormattedCounterValue();
return val.doubleValue / m_threads;
}
// Usage
// Monitor.exe is a test process here, which gives about 50-55% CPU usage in TaskManager
processor_time = std::make_unique<PdhCPUCounter>("\\Process(Monitor)\\% Processor Time");
// Here we're reported about 0.35 +/- 0.05
double cpu_utilization = processor_time->getCPUUtilization();
|
As I found out from the TechNet thread above, Microsoft utilizes
NtQuerySystemInformation
for the Task Manager implementation.
However, the call
|
NtQuerySystemInformation(SystemProcessInformation, processInfo, processInfoSize, NULL);
|
fills out values of a structure
1 2 3 4 5 6 7 8 9 10 11 12 13
|
typedef struct _SYSTEM_PROCESS_INFO
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
ULONG BasePriority;
HANDLE ProcessId;
HANDLE InheritedFromProcessId;
}SYSTEM_PROCESS_INFO,*PSYSTEM_PROCESS_INFO;
|
which also does not contain direct value for CPU utilization! Yes, it includes CreateTime, UserTime and KernelTime, however, provided these values, how to calculate CPU usage in a given period of time?