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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
// Copyright (C) 2019 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include "monitors/cpu_monitor.h"
#include <algorithm>
#ifdef _WIN32
#include "monitors/query_wrapper.h"
#include <string>
#include <system_error>
#include <PdhMsg.h>
#include <Windows.h>
namespace {
const std::size_t nCores = []() {
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}();
}
class CpuMonitor::PerformanceCounter {
public:
PerformanceCounter() : coreTimeCounters(nCores) {
PDH_STATUS status;
for (std::size_t i = 0; i < nCores; ++i) {
std::wstring fullCounterPath{L"\\Processor(" + std::to_wstring(i) + L")\\% Processor Time"};
status = PdhAddCounterW(query, fullCounterPath.c_str(), 0, &coreTimeCounters[i]);
if (ERROR_SUCCESS != status) {
throw std::system_error(status, std::system_category(), "PdhAddCounterW() failed");
}
status = PdhSetCounterScaleFactor(coreTimeCounters[i], -2); // scale counter to [0, 1]
if (ERROR_SUCCESS != status) {
throw std::system_error(status, std::system_category(), "PdhSetCounterScaleFactor() failed");
}
}
status = PdhCollectQueryData(query);
if (ERROR_SUCCESS != status) {
throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed");
}
}
std::vector<double> getCpuLoad() {
PDH_STATUS status;
status = PdhCollectQueryData(query);
if (ERROR_SUCCESS != status) {
throw std::system_error(status, std::system_category(), "PdhCollectQueryData() failed");
}
PDH_FMT_COUNTERVALUE displayValue;
std::vector<double> cpuLoad(coreTimeCounters.size());
for (std::size_t i = 0; i < coreTimeCounters.size(); ++i) {
status = PdhGetFormattedCounterValue(coreTimeCounters[i], PDH_FMT_DOUBLE, NULL,
&displayValue);
switch (status) {
case ERROR_SUCCESS: break;
// PdhGetFormattedCounterValue() can sometimes return PDH_CALC_NEGATIVE_DENOMINATOR for some reason
case PDH_CALC_NEGATIVE_DENOMINATOR: return {};
default:
throw std::system_error(status, std::system_category(), "PdhGetFormattedCounterValue() failed");
}
if (PDH_CSTATUS_VALID_DATA != displayValue.CStatus && PDH_CSTATUS_NEW_DATA != displayValue.CStatus) {
throw std::runtime_error("Error in counter data");
}
cpuLoad[i] = displayValue.doubleValue;
}
return cpuLoad;
}
private:
QueryWrapper query;
std::vector<PDH_HCOUNTER> coreTimeCounters;
};
#elif __linux__
#include <chrono>
#include <regex>
#include <utility>
#include <fstream>
#include <unistd.h>
namespace {
const long clockTicks = sysconf(_SC_CLK_TCK);
const std::size_t nCores = sysconf(_SC_NPROCESSORS_CONF);
std::vector<unsigned long> getIdleCpuStat() {
std::vector<unsigned long> idleCpuStat(nCores);
std::ifstream procStat("/proc/stat");
std::string line;
std::smatch match;
std::regex coreJiffies("^cpu(\\d+)\\s+"
"(\\d+)\\s+"
"(\\d+)\\s+"
"(\\d+)\\s+"
"(\\d+)\\s+" // idle
"(\\d+)"); // iowait
while (std::getline(procStat, line)) {
if (std::regex_search(line, match, coreJiffies)) {
// it doesn't handle overflow of sum and overflows of /proc/stat values
unsigned long idleInfo = stoul(match[5]) + stoul(match[6]),
coreId = stoul(match[1]);
if (nCores <= coreId) {
throw std::runtime_error("The number of cores has changed");
}
idleCpuStat[coreId] = idleInfo;
}
}
return idleCpuStat;
}
}
class CpuMonitor::PerformanceCounter {
public:
PerformanceCounter() : prevIdleCpuStat{getIdleCpuStat()}, prevTimePoint{std::chrono::steady_clock::now()} {}
std::vector<double> getCpuLoad() {
std::vector<unsigned long> idleCpuStat = getIdleCpuStat();
auto timePoint = std::chrono::steady_clock::now();
// don't update data too frequently which may result in negative values for cpuLoad.
// It may happen when collectData() is called just after setHistorySize().
if (timePoint - prevTimePoint > std::chrono::milliseconds{100}) {
std::vector<double> cpuLoad(nCores);
for (std::size_t i = 0; i < idleCpuStat.size(); ++i) {
double idleDiff = idleCpuStat[i] - prevIdleCpuStat[i];
typedef std::chrono::duration<double, std::chrono::seconds::period> Sec;
cpuLoad[i] = 1.0
- idleDiff / clockTicks / std::chrono::duration_cast<Sec>(timePoint - prevTimePoint).count();
}
prevIdleCpuStat = std::move(idleCpuStat);
prevTimePoint = timePoint;
return cpuLoad;
}
return {};
}
private:
std::vector<unsigned long> prevIdleCpuStat;
std::chrono::steady_clock::time_point prevTimePoint;
};
#else
// not implemented
namespace {
const std::size_t nCores{0};
}
class CpuMonitor::PerformanceCounter {
public:
std::vector<double> getCpuLoad() {return {};};
};
#endif
CpuMonitor::CpuMonitor() :
samplesNumber{0},
historySize{0},
cpuLoadSum(nCores, 0) {}
// PerformanceCounter is incomplete in header and destructor can't be defined implicitly
CpuMonitor::~CpuMonitor() = default;
void CpuMonitor::setHistorySize(std::size_t size) {
if (0 == historySize && 0 != size) {
performanceCounter.reset(new PerformanceCounter);
} else if (0 != historySize && 0 == size) {
performanceCounter.reset();
}
historySize = size;
std::ptrdiff_t newSize = static_cast<std::ptrdiff_t>(std::min(size, cpuLoadHistory.size()));
cpuLoadHistory.erase(cpuLoadHistory.begin(), cpuLoadHistory.end() - newSize);
}
void CpuMonitor::collectData() {
std::vector<double> cpuLoad = performanceCounter->getCpuLoad();
if (!cpuLoad.empty()) {
for (std::size_t i = 0; i < cpuLoad.size(); ++i) {
cpuLoadSum[i] += cpuLoad[i];
}
++samplesNumber;
cpuLoadHistory.push_back(std::move(cpuLoad));
if (cpuLoadHistory.size() > historySize) {
cpuLoadHistory.pop_front();
}
}
}
std::size_t CpuMonitor::getHistorySize() const {
return historySize;
}
std::deque<std::vector<double>> CpuMonitor::getLastHistory() const {
return cpuLoadHistory;
}
std::vector<double> CpuMonitor::getMeanCpuLoad() const {
std::vector<double> meanCpuLoad;
meanCpuLoad.reserve(cpuLoadSum.size());
for (double coreLoad : cpuLoadSum) {
meanCpuLoad.push_back(samplesNumber ? coreLoad / samplesNumber : 0);
}
return meanCpuLoad;
}
|