From 1a60999243b8bac53ded6abb5c6b9a8a27d2e57f Mon Sep 17 00:00:00 2001 From: FlightlessMango Date: Sun, 2 Feb 2020 00:55:24 +0100 Subject: [PATCH] New cpu implementation --- src/cpu.cpp | 208 ++++++++++++++++++++++++++++++++++++++++++++++++ src/cpu.h | 59 ++++++++++++++ src/cpu_gpu.h | 161 +------------------------------------ src/meson.build | 1 + src/overlay.cpp | 24 +++--- 5 files changed, 285 insertions(+), 168 deletions(-) create mode 100644 src/cpu.cpp create mode 100644 src/cpu.h diff --git a/src/cpu.cpp b/src/cpu.cpp new file mode 100644 index 00000000..2d8f67ff --- /dev/null +++ b/src/cpu.cpp @@ -0,0 +1,208 @@ +#include "cpu.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PROCDIR +#define PROCDIR "/proc" +#endif + +#ifndef PROCSTATFILE +#define PROCSTATFILE PROCDIR "/stat" +#endif + +#ifndef PROCMEMINFOFILE +#define PROCMEMINFOFILE PROCDIR "/meminfo" +#endif + +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + +static bool starts_with(const std::string& s, const char *t){ + return s.rfind(t, 0) == 0; +} + +std::vector coreMhz; + +CPUStats::CPUStats() +{ + m_inited = Init(); +} + +bool CPUStats::Init() +{ + std::string line; + std::ifstream file (PROCSTATFILE); + bool first = true; + m_cpuData.clear(); + + if (!file.is_open()) { + std::cerr << "Failed to opening " << PROCSTATFILE << std::endl; + return false; + } + + do { + if (!std::getline(file, line)) { + std::cerr << "Failed to read all of " << PROCSTATFILE << std::endl; + return false; + } else if (starts_with(line, "cpu")) { + if (first) { + first =false; + continue; + } + + CPUData cpu = {}; + cpu.totalTime = 1; + cpu.totalPeriod = 1; + m_cpuData.push_back(cpu); + + } else if (starts_with(line, "btime ")) { + + // C++ way, kind of noisy + //std::istringstream token( line ); + //std::string s; + //token >> s; + //token >> m_boottime; + + // assume that if btime got read, that everything else is OK too + sscanf(line.c_str(), "btime %lld\n", &m_boottime); + break; + } + } while(true); + + UpdateCPUData(); + return true; +} + +//TODO take sampling interval into account? +bool CPUStats::UpdateCPUData() +{ + CPUStats::UpdateCoreMhz(); + unsigned long long int usertime, nicetime, systemtime, idletime; + unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice; + int cpuid = -1; + + if (!m_inited) + return false; + + std::string line; + std::ifstream file (PROCSTATFILE); + bool ret = false; + + if (!file.is_open()) { + std::cerr << "Failed to opening " << PROCSTATFILE << std::endl; + return false; + } + + do { + if (!std::getline(file, line)) { + break; + } else if (!ret && sscanf(line.c_str(), "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", + &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice) == 10) { + ret = true; + } else if (sscanf(line.c_str(), "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", + &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice) == 11) { + + //std::cerr << "Parsing 'cpu" << cpuid << "' line:" << line << std::endl; + + if (!ret) { + //std::cerr << "Failed to parse 'cpu' line" << std::endl; + std::cerr << "Failed to parse 'cpu' line:" << line << std::endl; + return false; + } + + if (cpuid < 0 /* can it? */ || (size_t)cpuid > m_cpuData.size()) { + std::cerr << "Cpu id '" << cpuid << "' is out of bounds" << std::endl; + return false; + } + + CPUData& cpuData = m_cpuData[cpuid]; + + // Guest time is already accounted in usertime + usertime = usertime - guest; + nicetime = nicetime - guestnice; + // Fields existing on kernels >= 2.6 + // (and RHEL's patched kernel 2.4...) + unsigned long long int idlealltime = idletime + ioWait; + unsigned long long int systemalltime = systemtime + irq + softIrq; + unsigned long long int virtalltime = guest + guestnice; + unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime; + + // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t() + // used in /proc/stat rounds down numbers, it can lead to a case where the + // integer overflow. + #define WRAP_SUBTRACT(a,b) (a > b) ? a - b : 0 + cpuData.userPeriod = WRAP_SUBTRACT(usertime, cpuData.userTime); + cpuData.nicePeriod = WRAP_SUBTRACT(nicetime, cpuData.niceTime); + cpuData.systemPeriod = WRAP_SUBTRACT(systemtime, cpuData.systemTime); + cpuData.systemAllPeriod = WRAP_SUBTRACT(systemalltime, cpuData.systemAllTime); + cpuData.idleAllPeriod = WRAP_SUBTRACT(idlealltime, cpuData.idleAllTime); + cpuData.idlePeriod = WRAP_SUBTRACT(idletime, cpuData.idleTime); + cpuData.ioWaitPeriod = WRAP_SUBTRACT(ioWait, cpuData.ioWaitTime); + cpuData.irqPeriod = WRAP_SUBTRACT(irq, cpuData.irqTime); + cpuData.softIrqPeriod = WRAP_SUBTRACT(softIrq, cpuData.softIrqTime); + cpuData.stealPeriod = WRAP_SUBTRACT(steal, cpuData.stealTime); + cpuData.guestPeriod = WRAP_SUBTRACT(virtalltime, cpuData.guestTime); + cpuData.totalPeriod = WRAP_SUBTRACT(totaltime, cpuData.totalTime); + #undef WRAP_SUBTRACT + cpuData.userTime = usertime; + cpuData.niceTime = nicetime; + cpuData.systemTime = systemtime; + cpuData.systemAllTime = systemalltime; + cpuData.idleAllTime = idlealltime; + cpuData.idleTime = idletime; + cpuData.ioWaitTime = ioWait; + cpuData.irqTime = irq; + cpuData.softIrqTime = softIrq; + cpuData.stealTime = steal; + cpuData.guestTime = virtalltime; + cpuData.totalTime = totaltime; + cpuid = -1; + + float total = (float)(cpuData.totalPeriod == 0 ? 1 : cpuData.totalPeriod); + float v[4]; + v[0] = cpuData.nicePeriod * 100.0f / total; + v[1] = cpuData.userPeriod * 100.0f / total; + + /* if not detailed */ + v[2] = cpuData.systemAllPeriod * 100.0f / total; + v[3] = (cpuData.stealPeriod + cpuData.guestPeriod) * 100.0f / total; + cpuData.percent = std::min(std::max(v[0]+v[1]+v[2]+v[3], 0.0f), 100.0f); + + } else { + break; + } + } while(true); + + m_cpuPeriod = (double)m_cpuData[0].totalPeriod / m_cpuData.size(); + m_updatedCPUs = true; + return ret; +} + +bool CPUStats::UpdateCoreMhz() { + coreMhz.clear(); + FILE *cpuInfo = fopen(PROCCPUINFOFILE, "r"); + char line[256]; + int i = 0; + while (fgets(line, sizeof(line), cpuInfo)) { + CPUData& cpuData = m_cpuData[i]; + std::string row; + row = line; + if (row.find("MHz") != std::string::npos){ + row = std::regex_replace(row, std::regex(R"([^0-9.])"), ""); + cpuData.mhz = stoi(row); + i++; + } + } + + fclose(cpuInfo); + return true; +} + +CPUStats cpuStats; \ No newline at end of file diff --git a/src/cpu.h b/src/cpu.h new file mode 100644 index 00000000..c77977ce --- /dev/null +++ b/src/cpu.h @@ -0,0 +1,59 @@ +#include +#include + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int systemTime; + unsigned long long int systemAllTime; + unsigned long long int idleAllTime; + unsigned long long int idleTime; + unsigned long long int niceTime; + unsigned long long int ioWaitTime; + unsigned long long int irqTime; + unsigned long long int softIrqTime; + unsigned long long int stealTime; + unsigned long long int guestTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int systemPeriod; + unsigned long long int systemAllPeriod; + unsigned long long int idleAllPeriod; + unsigned long long int idlePeriod; + unsigned long long int nicePeriod; + unsigned long long int ioWaitPeriod; + unsigned long long int irqPeriod; + unsigned long long int softIrqPeriod; + unsigned long long int stealPeriod; + unsigned long long int guestPeriod; + float percent; + int mhz; +} CPUData; + +class CPUStats +{ +public: + CPUStats(); + bool Init(); + bool Updated() + { + return m_updatedCPUs; + } + + bool UpdateCPUData(); + bool UpdateCoreMhz(); + double GetCPUPeriod() { return m_cpuPeriod; } + + const std::vector& GetCPUData() const { + return m_cpuData; + } +private: + unsigned long long int m_boottime = 0; + std::vector m_cpuData; + double m_cpuPeriod = 0; + bool m_updatedCPUs = false; // TODO use caching or just update? + bool m_inited = false; +}; + +extern CPUStats cpuStats; \ No newline at end of file diff --git a/src/cpu_gpu.h b/src/cpu_gpu.h index c164babb..b5b4e2b3 100644 --- a/src/cpu_gpu.h +++ b/src/cpu_gpu.h @@ -23,19 +23,10 @@ int gpuLoad, gpuTemp, cpuTemp; string gpuLoadDisplay, cpuTempLocation; FILE *amdGpuFile, *amdTempFile, *cpuTempFile; -const int NUM_CPU_STATES = 10; - -struct Cpus{ - size_t num; - string name; - int value; - string output; - int freq; -}; int numCpuCores = std::thread::hardware_concurrency(); size_t arraySize = numCpuCores + 1; -std::vector cpuArray; +// std::vector cpuArray; pthread_t cpuThread, gpuThread, cpuInfoThread, nvidiaSmiThread; string exec(string command) { @@ -60,109 +51,6 @@ string exec(string command) { return result; } -void coreCounting(){ - cpuArray.push_back({0, "CPU:"}); - for (size_t i = 0; i < arraySize; i++) { - size_t offset = i; - stringstream ss; - ss << "CPU " << offset << ":"; - string cpuNameString = ss.str(); - cpuArray.push_back({i+1 , cpuNameString}); - } -} - -std::string m_cpuUtilizationString; - -enum CPUStates -{ - S_USER = 0, - S_NICE, - S_SYSTEM, - S_IDLE, - S_IOWAIT, - S_IRQ, - S_SOFTIRQ, - S_STEAL, - S_GUEST, - S_GUEST_NICE -}; - -typedef struct CPUData -{ - std::string cpu; - size_t times[NUM_CPU_STATES]; -} CPUData; - -void ReadStatsCPU(std::vector & entries) -{ - std::ifstream fileStat("/proc/stat"); - - std::string line; - - const std::string STR_CPU("cpu"); - const std::size_t LEN_STR_CPU = STR_CPU.size(); - const std::string STR_TOT("tot"); - - while(std::getline(fileStat, line)) - { - // cpu stats line found - if(!line.compare(0, LEN_STR_CPU, STR_CPU)) - { - std::istringstream ss(line); - - // store entry - entries.emplace_back(CPUData()); - CPUData & entry = entries.back(); - - // read cpu label - ss >> entry.cpu; - - if(entry.cpu.size() > LEN_STR_CPU) - entry.cpu.erase(0, LEN_STR_CPU); - else - entry.cpu = STR_TOT; - - // read times - for(int i = 0; i < NUM_CPU_STATES; ++i) - ss >> entry.times[i]; - } - } -} - -size_t GetIdleTime(const CPUData & e) -{ - return e.times[S_IDLE] + - e.times[S_IOWAIT]; -} - -size_t GetActiveTime(const CPUData & e) -{ - return e.times[S_USER] + - e.times[S_NICE] + - e.times[S_SYSTEM] + - e.times[S_IRQ] + - e.times[S_SOFTIRQ] + - e.times[S_STEAL] + - e.times[S_GUEST] + - e.times[S_GUEST_NICE]; -} - -void PrintStats(const std::vector & entries1, const std::vector & entries2) -{ - const size_t NUM_ENTRIES = entries1.size(); - - for(size_t i = 0; i < NUM_ENTRIES; ++i) - { - const CPUData & e1 = entries1[i]; - const CPUData & e2 = entries2[i]; - - const float ACTIVE_TIME = static_cast(GetActiveTime(e2) - GetActiveTime(e1)); - const float IDLE_TIME = static_cast(GetIdleTime(e2) - GetIdleTime(e1)); - const float TOTAL_TIME = ACTIVE_TIME + IDLE_TIME; - - cpuArray[i].value = (truncf(100.f * ACTIVE_TIME / TOTAL_TIME) * 10 / 10); - } -} void *cpuInfo(void *){ FILE *cpuInfo = fopen("/proc/cpuinfo", "r"); @@ -173,7 +61,7 @@ void *cpuInfo(void *){ row = line; if (row.find("MHz") != std::string::npos){ row = std::regex_replace(row, std::regex(R"([^0-9.])"), ""); - cpuArray[i + 1].freq = stoi(row); + // cpuArray[i + 1].freq = stoi(row); i++; } } @@ -223,48 +111,3 @@ void *getAmdGpuUsage(void *){ pthread_detach(gpuThread); return NULL; } - -void *getCpuUsage(void *) -{ - std::vector entries1; - std::vector entries2; - - // snapshot 1 - ReadStatsCPU(entries1); - - // 100ms pause - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - // snapshot 2 - ReadStatsCPU(entries2); - - // print output - PrintStats(entries1, entries2); - pthread_detach(cpuThread); - return NULL; -} - - -void updateCpuStrings(){ - for (size_t i = 0; i < arraySize; i++) { - size_t spacing = 10; - string value = to_string(cpuArray[i].value); - value.erase( value.find_last_not_of('0') + 1, std::string::npos ); - size_t correctionValue = (spacing - cpuArray[i].name.length()) - value.length(); - string correction = ""; - for (size_t i = 0; i < correctionValue; i++) { - correction.append(" "); - } - stringstream ss; - if (i < 11) { - if (i == 0) { - ss << cpuArray[i].name << " " << cpuArray[i].value << "%"; - } else { - ss << cpuArray[i].name << correction << cpuArray[i].value << "%"; - } - } else { - ss << cpuArray[i].name << correction << cpuArray[i].value << "%"; - } - cpuArray[i].output = ss.str(); - } - } \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 74730fe2..3e91a817 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,6 +35,7 @@ vklayer_files = files( 'overlay.cpp', 'overlay_params.c', 'font_unispace.c', + 'cpu.cpp', ) dep_nvml = cc.find_library('nvidia-ml', required: false) diff --git a/src/overlay.cpp b/src/overlay.cpp index 518a771a..0446b2bd 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -47,6 +47,7 @@ #include "cpu_gpu.h" #include "logging.h" #include "keybinds.h" +#include "cpu.h" bool open = false, displayHud = true; string gpuString; @@ -846,7 +847,7 @@ static void snapshot_swapchain_frame(struct swapchain_data *data) if(log_duration_env) duration = std::stoi(log_duration_env); - coreCounting(); + // coreCounting(); if (deviceName.find("Radeon") != std::string::npos || deviceName.find("AMD") != std::string::npos) { amdGpuFile = fopen("/sys/class/drm/card0/device/gpu_busy_percent", "r"); string tempFolder = exec("ls /sys/class/drm/card0/device/hwmon/"); @@ -907,10 +908,13 @@ static void snapshot_swapchain_frame(struct swapchain_data *data) if (data->last_fps_update) { if (capture_begin || elapsed >= instance_data->params.fps_sampling_period) { - updateCpuStrings(); - pthread_create(&cpuThread, NULL, &getCpuUsage, NULL); - data->cpuString = cpuArray[0].output.c_str(); - pthread_create(&cpuInfoThread, NULL, &cpuInfo, NULL); + cpuStats.UpdateCPUData(); + int i = 0; + cpuLoadLog = 0; + for (const CPUData &cpuData : cpuStats.GetCPUData()) + cpuLoadLog += cpuData.percent; + + cpuLoadLog = cpuLoadLog / cpuStats.GetCPUData().size(); // get gpu usage if (deviceName.find("GeForce") != std::string::npos) @@ -920,7 +924,7 @@ static void snapshot_swapchain_frame(struct swapchain_data *data) pthread_create(&gpuThread, NULL, &getAmdGpuUsage, NULL); // update variables for logging - cpuLoadLog = cpuArray[0].value; + // cpuLoadLog = cpuArray[0].value; gpuLoadLog = gpuLoad; data->frametimeDisplay = data->frametime; @@ -1090,7 +1094,8 @@ static void compute_swapchain_display(struct swapchain_data *data) } if (instance_data->params.enabled[OVERLAY_PARAM_ENABLED_core_load]){ - for (int i = 0; i < numCpuCores; i++) + int i = 0; + for (const CPUData &cpuData : cpuStats.GetCPUData()) { ImGui::TextColored(ImVec4(0.0, 0.502, 0.753, 1.00f), "CPU"); ImGui::SameLine(0, 1.0f); @@ -1098,13 +1103,14 @@ static void compute_swapchain_display(struct swapchain_data *data) ImGui::TextColored(ImVec4(0.0, 0.502, 0.753, 1.00f),"%i", i); ImGui::PopFont(); ImGui::SameLine(hudFirstRow); - ImGui::Text("%i%%", cpuArray[i + 1].value); + ImGui::Text("%i%%", int(cpuData.percent)); ImGui::SameLine(hudSecondRow); - ImGui::Text("%i", cpuArray[i + 1].freq); + ImGui::Text("%i", cpuData.mhz); ImGui::SameLine(0, 1.0f); ImGui::PushFont(font1); ImGui::Text("MHz"); ImGui::PopFont(); + i++; } } if (instance_data->params.enabled[OVERLAY_PARAM_ENABLED_fps]){