Skip to content

Commit 2691939

Browse files
fix: correct Linux cpu usage (#2039)
Co-authored-by: sangjanai <[email protected]>
1 parent c8503da commit 2691939

File tree

4 files changed

+184
-184
lines changed

4 files changed

+184
-184
lines changed

Diff for: engine/services/hardware_service.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ HardwareInfo HardwareService::GetHardwareInfo() {
5252
};
5353
}
5454

55-
return HardwareInfo{.cpu = cortex::hw::GetCPUInfo(),
55+
return HardwareInfo{.cpu = cpu_info_.GetCPUInfo(),
5656
.os = cortex::hw::GetOSInfo(),
5757
.ram = cortex::hw::GetMemoryInfo(),
5858
.storage = cortex::hw::GetStorageInfo(),

Diff for: engine/services/hardware_service.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#pragma once
22
#include <stdint.h>
3+
#include <mutex>
34
#include <string>
45
#include <vector>
5-
#include <mutex>
66

77
#include "common/hardware_config.h"
88
#include "database_service.h"
@@ -41,4 +41,5 @@ class HardwareService {
4141
std::shared_ptr<DatabaseService> db_service_ = nullptr;
4242
std::optional<cortex::hw::ActivateHardwareConfig> ahc_;
4343
std::mutex mtx_;
44+
cortex::hw::CpuInfo cpu_info_;
4445
};

Diff for: engine/utils/hardware/cpu_info.h

+181-20
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,198 @@
11
#pragma once
22

33
#include <json/json.h>
4+
#include <chrono>
5+
#include <iostream>
6+
#include <sstream>
47
#include <string>
58
#include <string_view>
9+
#include <thread>
610
#include <vector>
11+
12+
#ifdef _WIN32
13+
#include <pdh.h>
14+
#include <windows.h>
15+
#pragma comment(lib, "pdh.lib")
16+
#elif defined(__APPLE__) || defined(__MACH__)
17+
#include <mach/mach_host.h>
18+
#include <mach/mach_init.h>
19+
#else
20+
#include <unistd.h>
21+
#include <cmath>
22+
#include <fstream>
23+
#include <iterator>
24+
#endif
725
#include "common/hardware_common.h"
8-
#include "cpu_usage.h"
926
#include "hwinfo/hwinfo.h"
1027
#include "utils/cpuid/cpu_info.h"
1128

1229
namespace cortex::hw {
13-
inline CPU GetCPUInfo() {
14-
auto res = hwinfo::getAllCPUs();
15-
if (res.empty())
16-
return CPU{};
17-
auto cpu = res[0];
18-
cortex::cpuid::CpuInfo inst;
30+
struct Jiffies {
31+
Jiffies() {
32+
working = -1;
33+
all = -1;
34+
}
1935

20-
#if defined(__linux__)
21-
float usage = 0;
22-
for (auto const& c : res) {
23-
usage += c.currentUtilisation();
36+
Jiffies(int64_t _all, int64_t _working) {
37+
all = _all;
38+
working = _working;
2439
}
25-
usage = usage / res.size() * 100;
40+
41+
int64_t working;
42+
int64_t all;
43+
};
44+
45+
struct CpuInfo {
46+
private:
47+
cortex::cpuid::CpuInfo inst;
48+
bool jiffies_initialized = false;
49+
50+
public:
51+
double GetCPUUsage() {
52+
#if defined(_WIN32)
53+
unsigned long long previous_total_ticks = 0;
54+
unsigned long long previous_idle_ticks = 0;
55+
56+
auto calculate_cpu_load = [&](unsigned long long idle_ticks,
57+
unsigned long long total_ticks) {
58+
unsigned long long total_ticks_since_last_time =
59+
total_ticks - previous_total_ticks;
60+
unsigned long long idle_ticks_since_last_time =
61+
idle_ticks - previous_idle_ticks;
62+
63+
float ret = 1.0f - ((total_ticks_since_last_time > 0)
64+
? ((float)idle_ticks_since_last_time) /
65+
total_ticks_since_last_time
66+
: 0);
67+
68+
previous_total_ticks = total_ticks;
69+
previous_idle_ticks = idle_ticks;
70+
return ret * 100;
71+
};
72+
73+
auto file_time_to_int64 = [](const FILETIME& ft) {
74+
return (((unsigned long long)(ft.dwHighDateTime)) << 32) |
75+
((unsigned long long)ft.dwLowDateTime);
76+
};
77+
78+
FILETIME idle_time, kernel_time, user_time;
79+
float res = 0;
80+
constexpr const int kCount = 100;
81+
for (int i = 0; i < kCount; i++) {
82+
res += GetSystemTimes(&idle_time, &kernel_time, &user_time)
83+
? calculate_cpu_load(file_time_to_int64(idle_time),
84+
file_time_to_int64(kernel_time) +
85+
file_time_to_int64(user_time))
86+
: -1.0f;
87+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
88+
}
89+
return res < 0 ? -1.0f : res / kCount;
90+
91+
#elif defined(__APPLE__) || defined(__MACH__)
92+
// macOS implementation
93+
host_cpu_load_info_data_t cpu_info;
94+
mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
95+
96+
static unsigned long long previous_total_ticks = 0;
97+
static unsigned long long previous_idle_ticks = 0;
98+
99+
if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
100+
(host_info_t)&cpu_info, &count) == KERN_SUCCESS) {
101+
unsigned long long total_ticks = 0;
102+
for (int i = 0; i < CPU_STATE_MAX; i++) {
103+
total_ticks += cpu_info.cpu_ticks[i];
104+
}
105+
106+
unsigned long long idle_ticks = cpu_info.cpu_ticks[CPU_STATE_IDLE];
107+
108+
unsigned long long total_ticks_since_last_time =
109+
total_ticks - previous_total_ticks;
110+
unsigned long long idle_ticks_since_last_time =
111+
idle_ticks - previous_idle_ticks;
112+
113+
double cpu_usage = 1.0f - ((double)idle_ticks_since_last_time /
114+
total_ticks_since_last_time);
115+
116+
previous_total_ticks = total_ticks;
117+
previous_idle_ticks = idle_ticks;
118+
119+
return cpu_usage * 100.0;
120+
}
121+
return -1.0;
122+
26123
#else
27-
float usage = GetCPUUsage();
124+
if (!jiffies_initialized) {
125+
// Sleep 1 sec just for the start cause the usage needs to have a delta value which is depending on the unix file
126+
// read it's just for the init, you don't need to wait if the delta is already created ...
127+
std::this_thread::sleep_for(std::chrono::duration<double>(1));
128+
jiffies_initialized = true;
129+
}
130+
131+
auto get_jiffies = [](int index) -> Jiffies {
132+
std::ifstream filestat("/proc/stat");
133+
if (!filestat.is_open()) {
134+
return {};
135+
}
136+
137+
for (int i = 0; i < index; ++i) {
138+
if (!filestat.ignore(std::numeric_limits<std::streamsize>::max(),
139+
'\n')) {
140+
break;
141+
}
142+
}
143+
std::string line;
144+
std::getline(filestat, line);
145+
146+
std::istringstream iss(line);
147+
std::vector<std::string> results(std::istream_iterator<std::string>{iss},
148+
std::istream_iterator<std::string>());
149+
150+
const int64_t jiffies_0 = std::stol(results[1]);
151+
const int64_t jiffies_1 = std::stol(results[2]);
152+
const int64_t jiffies_2 = std::stol(results[3]);
153+
const int64_t jiffies_3 = std::stol(results[4]);
154+
const int64_t jiffies_4 = std::stol(results[5]);
155+
const int64_t jiffies_5 = std::stol(results[6]);
156+
const int64_t jiffies_6 = std::stol(results[7]);
157+
const int64_t jiffies_7 = std::stol(results[8]);
158+
const int64_t jiffies_8 = std::stol(results[9]);
159+
const int64_t jiffies_9 = std::stol(results[10]);
160+
161+
int64_t all = jiffies_0 + jiffies_1 + jiffies_2 + jiffies_3 + jiffies_4 +
162+
jiffies_5 + jiffies_6 + jiffies_7 + jiffies_8 + jiffies_9;
163+
int64_t working = jiffies_0 + jiffies_1 + jiffies_2;
164+
165+
return {all, working};
166+
};
167+
static Jiffies last = Jiffies();
168+
169+
Jiffies current = get_jiffies(0);
170+
171+
auto total_over_period = static_cast<double>(current.all - last.all);
172+
auto work_over_period = static_cast<double>(current.working - last.working);
173+
174+
last = current;
175+
176+
const double utilization = work_over_period / total_over_period;
177+
if (utilization < 0 || utilization > 1 || std::isnan(utilization)) {
178+
return -1.0;
179+
}
180+
return utilization * 100;
28181
#endif
182+
}
29183

30-
// float usage = 0;
31-
return CPU{.cores = cpu.numPhysicalCores(),
32-
.arch = std::string(GetArch()),
33-
.model = cpu.modelName(),
34-
.usage = usage,
35-
.instructions = inst.instructions()};
36-
}
184+
CPU GetCPUInfo() {
185+
auto res = hwinfo::getAllCPUs();
186+
if (res.empty())
187+
return CPU{};
188+
auto cpu = res[0];
189+
cortex::cpuid::CpuInfo inst;
190+
float usage = GetCPUUsage();
191+
return CPU{.cores = cpu.numPhysicalCores(),
192+
.arch = std::string(GetArch()),
193+
.model = cpu.modelName(),
194+
.usage = usage,
195+
.instructions = inst.instructions()};
196+
}
197+
};
37198
} // namespace cortex::hw

0 commit comments

Comments
 (0)