Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 193946e

Browse files
fix: add cpu usage (#1868)
Co-authored-by: vansangpfiev <sang@jan.ai>
1 parent e4e3e90 commit 193946e

File tree

4 files changed

+179
-7
lines changed

4 files changed

+179
-7
lines changed

engine/cli/commands/hardware_list_cmd.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ bool HardwareListCmd::Exec(const std::string& host, int port,
4141
if (!ho.has_value() || ho.value().show_cpu) {
4242
std::cout << "CPU Information:" << std::endl;
4343
Table table;
44-
std::vector<std::string> column_headers{"#", "Arch", "Cores", "Model",
45-
"Instructions"};
44+
std::vector<std::string> column_headers{"#", "Arch", "Cores",
45+
"Model", "Usage", "Instructions"};
4646

4747
Row_t header{column_headers.begin(), column_headers.end()};
4848
table.add_row(header);
@@ -52,6 +52,7 @@ bool HardwareListCmd::Exec(const std::string& host, int port,
5252
row.emplace_back(cpu.arch);
5353
row.emplace_back(std::to_string(cpu.cores));
5454
row.emplace_back(cpu.model);
55+
row.emplace_back(std::to_string(cpu.usage));
5556
std::string insts;
5657
for (auto const& i : cpu.instructions) {
5758
insts += i + " ";

engine/common/hardware_common.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#pragma once
2+
#include <assert.h>
23
#include <json/json.h>
34
#include <string>
45
#include <variant>
56
#include <vector>
6-
#include <assert.h>
77

88
namespace cortex::hw {
99

@@ -26,6 +26,7 @@ struct CPU {
2626
int cores;
2727
std::string arch;
2828
std::string model;
29+
float usage;
2930
std::vector<std::string> instructions;
3031
};
3132

@@ -34,6 +35,7 @@ inline Json::Value ToJson(const CPU& cpu) {
3435
res["arch"] = cpu.arch;
3536
res["cores"] = cpu.cores;
3637
res["model"] = cpu.model;
38+
res["usage"] = cpu.usage;
3739
Json::Value insts(Json::arrayValue);
3840
for (auto const& i : cpu.instructions) {
3941
insts.append(i);
@@ -47,11 +49,16 @@ inline CPU FromJson(const Json::Value& root) {
4749
int cores = root["cores"].asInt();
4850
std::string arch = root["arch"].asString();
4951
std::string model = root["model"].asString();
52+
float usage = root["usage"].asFloat();
5053
std::vector<std::string> insts;
5154
for (auto const& i : root["instructions"]) {
5255
insts.emplace_back(i.asString());
5356
}
54-
return {.cores = cores, .arch = arch, .model = model, .instructions = insts};
57+
return {.cores = cores,
58+
.arch = arch,
59+
.model = model,
60+
.usage = usage,
61+
.instructions = insts};
5562
}
5663
} // namespace cpu
5764

@@ -143,7 +150,6 @@ inline OS FromJson(const Json::Value& root) {
143150
}
144151
} // namespace os
145152

146-
147153
struct PowerInfo {
148154
std::string charging_status;
149155
int battery_life;
@@ -166,7 +172,6 @@ inline PowerInfo FromJson(const Json::Value& root) {
166172
}
167173
} // namespace power
168174

169-
170175
namespace {
171176
int64_t ByteToMiB(int64_t b) {
172177
return b / 1024 / 1024;
@@ -215,4 +220,4 @@ inline StorageInfo FromJson(const Json::Value& root) {
215220
.available = root["available"].asInt64()};
216221
}
217222
} // namespace storage
218-
}
223+
} // namespace cortex::hw

engine/utils/hardware/cpu_info.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string_view>
66
#include <vector>
77
#include "common/hardware_common.h"
8+
#include "cpu_usage.h"
89
#include "hwinfo/hwinfo.h"
910
#include "utils/cpuid/cpu_info.h"
1011

@@ -15,9 +16,12 @@ inline CPU GetCPUInfo() {
1516
return CPU{};
1617
auto cpu = res[0];
1718
cortex::cpuid::CpuInfo inst;
19+
float usage = GetCPUUsage();
20+
// float usage = 0;
1821
return CPU{.cores = cpu.numPhysicalCores(),
1922
.arch = std::string(GetArch()),
2023
.model = cpu.modelName(),
24+
.usage = usage,
2125
.instructions = inst.instructions()};
2226
}
2327
} // namespace cortex::hw

engine/utils/hardware/cpu_usage.h

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#pragma once
2+
#include <chrono>
3+
#include <iostream>
4+
#include <sstream>
5+
#include <thread>
6+
#include <vector>
7+
8+
#ifdef _WIN32
9+
#include <pdh.h>
10+
#include <windows.h>
11+
#pragma comment(lib, "pdh.lib")
12+
#elif defined(__APPLE__) || defined(__MACH__)
13+
#include <mach/mach_host.h>
14+
#include <mach/mach_init.h>
15+
#else
16+
#include <unistd.h>
17+
#include <fstream>
18+
#endif
19+
#include "utils/logging_utils.h"
20+
21+
namespace cortex::hw {
22+
inline double GetCPUUsage() {
23+
#if defined(_WIN32)
24+
unsigned long long previous_total_ticks = 0;
25+
unsigned long long previous_idle_ticks = 0;
26+
27+
auto calculate_cpu_load = [&](unsigned long long idle_ticks,
28+
unsigned long long total_ticks) {
29+
unsigned long long total_ticks_since_last_time =
30+
total_ticks - previous_total_ticks;
31+
unsigned long long idle_ticks_since_last_time =
32+
idle_ticks - previous_idle_ticks;
33+
34+
float ret = 1.0f - ((total_ticks_since_last_time > 0)
35+
? ((float)idle_ticks_since_last_time) /
36+
total_ticks_since_last_time
37+
: 0);
38+
39+
previous_total_ticks = total_ticks;
40+
previous_idle_ticks = idle_ticks;
41+
return ret * 100;
42+
};
43+
44+
auto file_time_to_int64 = [](const FILETIME& ft) {
45+
return (((unsigned long long)(ft.dwHighDateTime)) << 32) |
46+
((unsigned long long)ft.dwLowDateTime);
47+
};
48+
49+
FILETIME idle_time, kernel_time, user_time;
50+
float res = 0;
51+
constexpr const int kCount = 100;
52+
for (int i = 0; i < kCount; i++) {
53+
res += GetSystemTimes(&idle_time, &kernel_time, &user_time)
54+
? calculate_cpu_load(file_time_to_int64(idle_time),
55+
file_time_to_int64(kernel_time) +
56+
file_time_to_int64(user_time))
57+
: -1.0f;
58+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
59+
}
60+
return res < 0 ? -1.0f : res / kCount;
61+
62+
#elif defined(__APPLE__) || defined(__MACH__)
63+
// macOS implementation
64+
host_cpu_load_info_data_t cpu_info;
65+
mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
66+
67+
static unsigned long long previous_total_ticks = 0;
68+
static unsigned long long previous_idle_ticks = 0;
69+
70+
if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
71+
(host_info_t)&cpu_info, &count) == KERN_SUCCESS) {
72+
unsigned long long total_ticks = 0;
73+
for (int i = 0; i < CPU_STATE_MAX; i++) {
74+
total_ticks += cpu_info.cpu_ticks[i];
75+
}
76+
77+
unsigned long long idle_ticks = cpu_info.cpu_ticks[CPU_STATE_IDLE];
78+
79+
unsigned long long total_ticks_since_last_time =
80+
total_ticks - previous_total_ticks;
81+
unsigned long long idle_ticks_since_last_time =
82+
idle_ticks - previous_idle_ticks;
83+
84+
double cpu_usage = 1.0f - ((double)idle_ticks_since_last_time /
85+
total_ticks_since_last_time);
86+
87+
previous_total_ticks = total_ticks;
88+
previous_idle_ticks = idle_ticks;
89+
90+
return cpu_usage * 100.0;
91+
}
92+
return -1.0;
93+
94+
#else
95+
// Linux implementation
96+
std::vector<unsigned long long> last_total_user, last_total_user_low,
97+
last_total_sys, last_total_idle;
98+
99+
std::vector<unsigned long long> total_user, total_user_low, total_sys,
100+
total_idle;
101+
102+
std::ifstream stat_file("/proc/stat");
103+
if (stat_file.is_open()) {
104+
std::string line;
105+
int cpu_count = 0;
106+
double total_cpu_percentage = 0.0;
107+
108+
while (std::getline(stat_file, line)) {
109+
if (line.substr(0, 3) != "cpu")
110+
break; // We only want lines that start with "cpu"
111+
112+
cpu_count++;
113+
std::vector<unsigned long long> values;
114+
std::istringstream iss(line);
115+
std::string cpu;
116+
iss >> cpu;
117+
unsigned long long value;
118+
while (iss >> value) {
119+
values.push_back(value);
120+
}
121+
122+
if (values.size() < 4)
123+
continue;
124+
125+
total_user.push_back(values[0]);
126+
total_user_low.push_back(values[1]);
127+
total_sys.push_back(values[2]);
128+
total_idle.push_back(values[3]);
129+
130+
if (last_total_user.size() < cpu_count) {
131+
last_total_user.push_back(0);
132+
last_total_user_low.push_back(0);
133+
last_total_sys.push_back(0);
134+
last_total_idle.push_back(0);
135+
}
136+
137+
unsigned long long total =
138+
(total_user[cpu_count - 1] - last_total_user[cpu_count - 1]) +
139+
(total_user_low[cpu_count - 1] - last_total_user_low[cpu_count - 1]) +
140+
(total_sys[cpu_count - 1] - last_total_sys[cpu_count - 1]);
141+
double percent = total;
142+
total += (total_idle[cpu_count - 1] - last_total_idle[cpu_count - 1]);
143+
percent /= total;
144+
percent *= 100;
145+
146+
total_cpu_percentage += percent;
147+
148+
last_total_user[cpu_count - 1] = total_user[cpu_count - 1];
149+
last_total_user_low[cpu_count - 1] = total_user_low[cpu_count - 1];
150+
last_total_sys[cpu_count - 1] = total_sys[cpu_count - 1];
151+
last_total_idle[cpu_count - 1] = total_idle[cpu_count - 1];
152+
}
153+
stat_file.close();
154+
155+
if (cpu_count > 0) {
156+
return total_cpu_percentage / cpu_count; // Return average CPU usage
157+
}
158+
}
159+
return -1.0;
160+
#endif
161+
}
162+
} // namespace cortex::hw

0 commit comments

Comments
 (0)