From a4b52dad7f26375c518a6c616696f1bc4b87c7d8 Mon Sep 17 00:00:00 2001 From: vansangpfiev Date: Thu, 16 Jan 2025 16:38:17 +0700 Subject: [PATCH] fix: add cpu usage --- engine/cli/commands/hardware_list_cmd.cc | 5 +- engine/common/hardware_common.h | 15 ++- engine/utils/hardware/cpu_info.h | 4 + engine/utils/hardware/cpu_usage.h | 162 +++++++++++++++++++++++ 4 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 engine/utils/hardware/cpu_usage.h diff --git a/engine/cli/commands/hardware_list_cmd.cc b/engine/cli/commands/hardware_list_cmd.cc index 5c78c4bf9..5a67cea8b 100644 --- a/engine/cli/commands/hardware_list_cmd.cc +++ b/engine/cli/commands/hardware_list_cmd.cc @@ -41,8 +41,8 @@ bool HardwareListCmd::Exec(const std::string& host, int port, if (!ho.has_value() || ho.value().show_cpu) { std::cout << "CPU Information:" << std::endl; Table table; - std::vector column_headers{"#", "Arch", "Cores", "Model", - "Instructions"}; + std::vector column_headers{"#", "Arch", "Cores", + "Model", "Usage", "Instructions"}; Row_t header{column_headers.begin(), column_headers.end()}; table.add_row(header); @@ -52,6 +52,7 @@ bool HardwareListCmd::Exec(const std::string& host, int port, row.emplace_back(cpu.arch); row.emplace_back(std::to_string(cpu.cores)); row.emplace_back(cpu.model); + row.emplace_back(std::to_string(cpu.usage)); std::string insts; for (auto const& i : cpu.instructions) { insts += i + " "; diff --git a/engine/common/hardware_common.h b/engine/common/hardware_common.h index e9cbee1be..885e1d4b6 100644 --- a/engine/common/hardware_common.h +++ b/engine/common/hardware_common.h @@ -1,9 +1,9 @@ #pragma once +#include #include #include #include #include -#include namespace cortex::hw { @@ -26,6 +26,7 @@ struct CPU { int cores; std::string arch; std::string model; + float usage; std::vector instructions; }; @@ -34,6 +35,7 @@ inline Json::Value ToJson(const CPU& cpu) { res["arch"] = cpu.arch; res["cores"] = cpu.cores; res["model"] = cpu.model; + res["usage"] = cpu.usage; Json::Value insts(Json::arrayValue); for (auto const& i : cpu.instructions) { insts.append(i); @@ -47,11 +49,16 @@ inline CPU FromJson(const Json::Value& root) { int cores = root["cores"].asInt(); std::string arch = root["arch"].asString(); std::string model = root["model"].asString(); + float usage = root["usage"].asFloat(); std::vector insts; for (auto const& i : root["instructions"]) { insts.emplace_back(i.asString()); } - return {.cores = cores, .arch = arch, .model = model, .instructions = insts}; + return {.cores = cores, + .arch = arch, + .model = model, + .usage = usage, + .instructions = insts}; } } // namespace cpu @@ -143,7 +150,6 @@ inline OS FromJson(const Json::Value& root) { } } // namespace os - struct PowerInfo { std::string charging_status; int battery_life; @@ -166,7 +172,6 @@ inline PowerInfo FromJson(const Json::Value& root) { } } // namespace power - namespace { int64_t ByteToMiB(int64_t b) { return b / 1024 / 1024; @@ -215,4 +220,4 @@ inline StorageInfo FromJson(const Json::Value& root) { .available = root["available"].asInt64()}; } } // namespace storage -} \ No newline at end of file +} // namespace cortex::hw \ No newline at end of file diff --git a/engine/utils/hardware/cpu_info.h b/engine/utils/hardware/cpu_info.h index 4395cc8dd..396184fa6 100644 --- a/engine/utils/hardware/cpu_info.h +++ b/engine/utils/hardware/cpu_info.h @@ -5,6 +5,7 @@ #include #include #include "common/hardware_common.h" +#include "cpu_usage.h" #include "hwinfo/hwinfo.h" #include "utils/cpuid/cpu_info.h" @@ -15,9 +16,12 @@ inline CPU GetCPUInfo() { return CPU{}; auto cpu = res[0]; cortex::cpuid::CpuInfo inst; + float usage = GetCPUUsage(); + // float usage = 0; return CPU{.cores = cpu.numPhysicalCores(), .arch = std::string(GetArch()), .model = cpu.modelName(), + .usage = usage, .instructions = inst.instructions()}; } } // namespace cortex::hw \ No newline at end of file diff --git a/engine/utils/hardware/cpu_usage.h b/engine/utils/hardware/cpu_usage.h new file mode 100644 index 000000000..2bba32f09 --- /dev/null +++ b/engine/utils/hardware/cpu_usage.h @@ -0,0 +1,162 @@ +#pragma once +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "pdh.lib") +#elif defined(__APPLE__) || defined(__MACH__) +#include +#include +#else +#include +#include +#endif +#include "utils/logging_utils.h" + +namespace cortex::hw { +inline double GetCPUUsage() { +#if defined(_WIN32) + unsigned long long previous_total_ticks = 0; + unsigned long long previous_idle_ticks = 0; + + auto calculate_cpu_load = [&](unsigned long long idle_ticks, + unsigned long long total_ticks) { + unsigned long long total_ticks_since_last_time = + total_ticks - previous_total_ticks; + unsigned long long idle_ticks_since_last_time = + idle_ticks - previous_idle_ticks; + + float ret = 1.0f - ((total_ticks_since_last_time > 0) + ? ((float)idle_ticks_since_last_time) / + total_ticks_since_last_time + : 0); + + previous_total_ticks = total_ticks; + previous_idle_ticks = idle_ticks; + return ret * 100; + }; + + auto file_time_to_int64 = [](const FILETIME& ft) { + return (((unsigned long long)(ft.dwHighDateTime)) << 32) | + ((unsigned long long)ft.dwLowDateTime); + }; + + FILETIME idle_time, kernel_time, user_time; + float res = 0; + constexpr const int kCount = 100; + for (int i = 0; i < kCount; i++) { + res += GetSystemTimes(&idle_time, &kernel_time, &user_time) + ? calculate_cpu_load(file_time_to_int64(idle_time), + file_time_to_int64(kernel_time) + + file_time_to_int64(user_time)) + : -1.0f; + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return res < 0 ? -1.0f : res / kCount; + +#elif defined(__APPLE__) || defined(__MACH__) + // macOS implementation + host_cpu_load_info_data_t cpu_info; + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + + static unsigned long long previous_total_ticks = 0; + static unsigned long long previous_idle_ticks = 0; + + if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, + (host_info_t)&cpu_info, &count) == KERN_SUCCESS) { + unsigned long long total_ticks = 0; + for (int i = 0; i < CPU_STATE_MAX; i++) { + total_ticks += cpu_info.cpu_ticks[i]; + } + + unsigned long long idle_ticks = cpu_info.cpu_ticks[CPU_STATE_IDLE]; + + unsigned long long total_ticks_since_last_time = + total_ticks - previous_total_ticks; + unsigned long long idle_ticks_since_last_time = + idle_ticks - previous_idle_ticks; + + double cpu_usage = 1.0f - ((double)idle_ticks_since_last_time / + total_ticks_since_last_time); + + previous_total_ticks = total_ticks; + previous_idle_ticks = idle_ticks; + + return cpu_usage * 100.0; + } + return -1.0; + +#else + // Linux implementation + std::vector last_total_user, last_total_user_low, + last_total_sys, last_total_idle; + + std::vector total_user, total_user_low, total_sys, + total_idle; + + std::ifstream stat_file("/proc/stat"); + if (stat_file.is_open()) { + std::string line; + int cpu_count = 0; + double total_cpu_percentage = 0.0; + + while (std::getline(stat_file, line)) { + if (line.substr(0, 3) != "cpu") + break; // We only want lines that start with "cpu" + + cpu_count++; + std::vector values; + std::istringstream iss(line); + std::string cpu; + iss >> cpu; + unsigned long long value; + while (iss >> value) { + values.push_back(value); + } + + if (values.size() < 4) + continue; + + total_user.push_back(values[0]); + total_user_low.push_back(values[1]); + total_sys.push_back(values[2]); + total_idle.push_back(values[3]); + + if (last_total_user.size() < cpu_count) { + last_total_user.push_back(0); + last_total_user_low.push_back(0); + last_total_sys.push_back(0); + last_total_idle.push_back(0); + } + + unsigned long long total = + (total_user[cpu_count - 1] - last_total_user[cpu_count - 1]) + + (total_user_low[cpu_count - 1] - last_total_user_low[cpu_count - 1]) + + (total_sys[cpu_count - 1] - last_total_sys[cpu_count - 1]); + double percent = total; + total += (total_idle[cpu_count - 1] - last_total_idle[cpu_count - 1]); + percent /= total; + percent *= 100; + + total_cpu_percentage += percent; + + last_total_user[cpu_count - 1] = total_user[cpu_count - 1]; + last_total_user_low[cpu_count - 1] = total_user_low[cpu_count - 1]; + last_total_sys[cpu_count - 1] = total_sys[cpu_count - 1]; + last_total_idle[cpu_count - 1] = total_idle[cpu_count - 1]; + } + stat_file.close(); + + if (cpu_count > 0) { + return total_cpu_percentage / cpu_count; // Return average CPU usage + } + } + return -1.0; +#endif +} +} // namespace cortex::hw \ No newline at end of file