From 4b0600d8f8f1f6aaae6fc2e59e8697ff8779cf64 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Fri, 9 Aug 2019 21:34:48 -0700 Subject: [PATCH 1/7] Add new display option to also show CPU frequency in CPU meters. The option is only implemented on Linux. On other platforms, and on Linuxes that do not expose the relevant sysfs file, the frequency will be 0. The "CPU average" meter does not show a frequency, only the individual per-CPU meters. --- CPUMeter.c | 20 ++++++++++++++++++-- CPUMeter.h | 3 ++- DisplayOptionsPanel.c | 1 + Settings.c | 5 +++++ Settings.h | 1 + linux/Platform.c | 16 ++++++++++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CPUMeter.c b/CPUMeter.c index de5490df..6f9419b5 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -28,7 +28,8 @@ typedef enum { CPU_METER_STEAL = 5, CPU_METER_GUEST = 6, CPU_METER_IOWAIT = 7, - CPU_METER_ITEMCOUNT = 8, // number of entries in this enum + CPU_METER_FREQUENCY = 8, + CPU_METER_ITEMCOUNT = 9, // number of entries in this enum } CPUMeterValues; }*/ @@ -63,7 +64,22 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { } memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); double percent = Platform_setCPUValues(this, cpu); - xSnprintf(buffer, size, "%5.1f%%", percent); + if (cpu != 0 && this->pl->settings->showCPUFrequency) { + /* Initial frequency is in KHz. Divide it by 1024 till it's less than 1024, and emit unit accordingly */ + double cpuFrequency = this->values[CPU_METER_FREQUENCY]; + char unit = 'K'; + if (cpuFrequency > 1024) { + cpuFrequency /= 1024; + unit = 'M'; + } + if (cpuFrequency > 1024) { + cpuFrequency /= 1024; + unit = 'G'; + } + xSnprintf(buffer, size, "%5.1f%% %.1f%cHz", percent, cpuFrequency, unit); + } else { + xSnprintf(buffer, size, "%5.1f%%", percent); + } } static void CPUMeter_display(Object* cast, RichString* out) { diff --git a/CPUMeter.h b/CPUMeter.h index 2f163968..6f8599a8 100644 --- a/CPUMeter.h +++ b/CPUMeter.h @@ -20,7 +20,8 @@ typedef enum { CPU_METER_STEAL = 5, CPU_METER_GUEST = 6, CPU_METER_IOWAIT = 7, - CPU_METER_ITEMCOUNT = 8, // number of entries in this enum + CPU_METER_FREQUENCY = 8, + CPU_METER_ITEMCOUNT = 9, // number of entries in this enum } CPUMeterValues; diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 0ff54e33..0c741684 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -97,5 +97,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); + Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency))); return this; } diff --git a/Settings.c b/Settings.c index db2fa066..a268ff4d 100644 --- a/Settings.c +++ b/Settings.c @@ -45,6 +45,7 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; @@ -223,6 +224,8 @@ static bool Settings_read(Settings* this, const char* fileName) { this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_zero")) { this->countCPUsFromZero = atoi(option[1]); + } else if (String_eq(option[0], "show_cpu_frequency")) { + this->showCPUFrequency = atoi(option[1]); } else if (String_eq(option[0], "update_process_names")) { this->updateProcessNames = atoi(option[1]); } else if (String_eq(option[0], "account_guest_in_cpu_meter")) { @@ -312,6 +315,7 @@ bool Settings_write(Settings* this) { fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); + fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); @@ -340,6 +344,7 @@ Settings* Settings_new(int cpuCount) { this->highlightMegabytes = false; this->detailedCPUTime = false; this->countCPUsFromZero = false; + this->showCPUFrequency = false; this->updateProcessNames = false; this->cpuCount = cpuCount; this->showProgramPath = true; diff --git a/Settings.h b/Settings.h index d9dc0683..54b15fce 100644 --- a/Settings.h +++ b/Settings.h @@ -36,6 +36,7 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; diff --git a/linux/Platform.c b/linux/Platform.c index ab90ca74..e92d0c51 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -192,6 +192,22 @@ double Platform_setCPUValues(Meter* this, int cpu) { } percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = 0; + if (this->pl->settings->showCPUFrequency) { + char filename[63+1]; + xSnprintf(filename, 63, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu - 1); + FILE* fd = fopen(filename, "r"); + if (fd) { + unsigned int cpuFrequency; + int n = fscanf(fd, "%u", &cpuFrequency); + fclose(fd); + if (n > 0) { + v[CPU_METER_FREQUENCY] = cpuFrequency; + } + } + } + return percent; } From 9703a25d1b30a25aead4802fecd462dfac36e011 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Fri, 9 Aug 2019 23:22:05 -0700 Subject: [PATCH 2/7] Divide by 1000, not 1024, and show more decimals. --- CPUMeter.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CPUMeter.c b/CPUMeter.c index 6f9419b5..f9984ffa 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -65,18 +65,18 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); double percent = Platform_setCPUValues(this, cpu); if (cpu != 0 && this->pl->settings->showCPUFrequency) { - /* Initial frequency is in KHz. Divide it by 1024 till it's less than 1024, and emit unit accordingly */ + /* Initial frequency is in KHz. Divide it by 1000 till it's less than 1000, and emit unit accordingly */ double cpuFrequency = this->values[CPU_METER_FREQUENCY]; char unit = 'K'; - if (cpuFrequency > 1024) { - cpuFrequency /= 1024; + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; unit = 'M'; } - if (cpuFrequency > 1024) { - cpuFrequency /= 1024; + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; unit = 'G'; } - xSnprintf(buffer, size, "%5.1f%% %.1f%cHz", percent, cpuFrequency, unit); + xSnprintf(buffer, size, "%5.1f%% %.3f%cHz", percent, cpuFrequency, unit); } else { xSnprintf(buffer, size, "%5.1f%%", percent); } From 1d5e6a27a05ed44e6364a5b14c925503f8604a1f Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 10 Aug 2019 11:20:21 -0700 Subject: [PATCH 3/7] Add a display option to hide CPU usage number from CPU meter. --- CPUMeter.c | 10 ++++++++-- DisplayOptionsPanel.c | 1 + Settings.c | 5 +++++ Settings.h | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CPUMeter.c b/CPUMeter.c index f9984ffa..ef01e0ff 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -76,9 +76,15 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { cpuFrequency /= 1000; unit = 'G'; } - xSnprintf(buffer, size, "%5.1f%% %.3f%cHz", percent, cpuFrequency, unit); - } else { + if (this->pl->settings->showCPUUsage) { + xSnprintf(buffer, size, "%5.1f%% %.3f%cHz", percent, cpuFrequency, unit); + } else { + xSnprintf(buffer, size, "%.3f%cHz", cpuFrequency, unit); + } + } else if (this->pl->settings->showCPUUsage) { xSnprintf(buffer, size, "%5.1f%%", percent); + } else if (size > 0) { + buffer[0] = '\0'; } } diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index 0c741684..6c10eabe 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -97,6 +97,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter))); + Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency))); return this; } diff --git a/Settings.c b/Settings.c index a268ff4d..b9535e6b 100644 --- a/Settings.c +++ b/Settings.c @@ -45,6 +45,7 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; bool showCPUFrequency; bool treeView; bool showProgramPath; @@ -224,6 +225,8 @@ static bool Settings_read(Settings* this, const char* fileName) { this->detailedCPUTime = atoi(option[1]); } else if (String_eq(option[0], "cpu_count_from_zero")) { this->countCPUsFromZero = atoi(option[1]); + } else if (String_eq(option[0], "show_cpu_usage")) { + this->showCPUUsage = atoi(option[1]); } else if (String_eq(option[0], "show_cpu_frequency")) { this->showCPUFrequency = atoi(option[1]); } else if (String_eq(option[0], "update_process_names")) { @@ -315,6 +318,7 @@ bool Settings_write(Settings* this) { fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero); + fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage); fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames); fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter); @@ -344,6 +348,7 @@ Settings* Settings_new(int cpuCount) { this->highlightMegabytes = false; this->detailedCPUTime = false; this->countCPUsFromZero = false; + this->showCPUUsage = true; this->showCPUFrequency = false; this->updateProcessNames = false; this->cpuCount = cpuCount; diff --git a/Settings.h b/Settings.h index 54b15fce..0490faef 100644 --- a/Settings.h +++ b/Settings.h @@ -36,6 +36,7 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; bool showCPUFrequency; bool treeView; bool showProgramPath; From 1acfb0a75223068c676e06967003fe989400e21d Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 10 Aug 2019 11:37:35 -0700 Subject: [PATCH 4/7] Show N/A instead of 0KHz when CPU frequency is not available. --- CPUMeter.c | 24 +++++++++++++++--------- linux/Platform.c | 6 +++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CPUMeter.c b/CPUMeter.c index ef01e0ff..561f5132 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -68,18 +68,24 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { /* Initial frequency is in KHz. Divide it by 1000 till it's less than 1000, and emit unit accordingly */ double cpuFrequency = this->values[CPU_METER_FREQUENCY]; char unit = 'K'; - if (cpuFrequency > 1000) { - cpuFrequency /= 1000; - unit = 'M'; - } - if (cpuFrequency > 1000) { - cpuFrequency /= 1000; - unit = 'G'; + char cpuFrequencyBuffer[16]; + if (cpuFrequency < 0) { + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); + } else { + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; + unit = 'M'; + } + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; + unit = 'G'; + } + xSnprintf(cpuFrequencyBuffer, 15, "%.3f%cHz", cpuFrequency, unit); } if (this->pl->settings->showCPUUsage) { - xSnprintf(buffer, size, "%5.1f%% %.3f%cHz", percent, cpuFrequency, unit); + xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer); } else { - xSnprintf(buffer, size, "%.3f%cHz", cpuFrequency, unit); + xSnprintf(buffer, size, "%s", cpuFrequencyBuffer); } } else if (this->pl->settings->showCPUUsage) { xSnprintf(buffer, size, "%5.1f%%", percent); diff --git a/linux/Platform.c b/linux/Platform.c index e92d0c51..fc81d3bf 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -193,10 +193,10 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; - v[CPU_METER_FREQUENCY] = 0; + v[CPU_METER_FREQUENCY] = -1; if (this->pl->settings->showCPUFrequency) { - char filename[63+1]; - xSnprintf(filename, 63, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu - 1); + char filename[64]; + xSnprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu - 1); FILE* fd = fopen(filename, "r"); if (fd) { unsigned int cpuFrequency; From 43728b37e76e4ed32b04c133ca55e27116cdfa06 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 10 Aug 2019 11:46:21 -0700 Subject: [PATCH 5/7] Fix typo. --- CPUMeter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CPUMeter.c b/CPUMeter.c index 561f5132..edcd61d2 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -80,7 +80,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { cpuFrequency /= 1000; unit = 'G'; } - xSnprintf(cpuFrequencyBuffer, 15, "%.3f%cHz", cpuFrequency, unit); + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit); } if (this->pl->settings->showCPUUsage) { xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer); From 909bb86f05c20f0ec6f5d55994065a293932ce21 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 10 Aug 2019 17:17:45 -0700 Subject: [PATCH 6/7] Show N/A on unsupported platforms instead of 0KHz --- darwin/Platform.c | 2 ++ dragonflybsd/Platform.c | 3 +++ freebsd/Platform.c | 3 +++ openbsd/Platform.c | 2 ++ solaris/Platform.c | 3 +++ unsupported/Platform.c | 5 ++++- 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/darwin/Platform.c b/darwin/Platform.c index 1dce8b67..cd45bedf 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -217,6 +217,8 @@ double Platform_setCPUValues(Meter* mtr, int cpu) { /* Convert to percent and return */ total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL]; + mtr->values[CPU_METER_FREQUENCY] = -1; + return CLAMP(total, 0.0, 100.0); } diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 370943d7..250e50e7 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -179,6 +179,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 5dd6ca41..16d02329 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -178,6 +178,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 4bb2e35e..e5a67d9f 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -233,6 +233,8 @@ double Platform_setCPUValues(Meter* this, int cpu) { perc = v[0] + v[1] + v[2] + v[3]; + v[CPU_METER_FREQUENCY] = -1; + if (perc <= 100. && perc >= 0.) { return perc; } else { diff --git a/solaris/Platform.c b/solaris/Platform.c index a29fcb47..c180d988 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -203,6 +203,9 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; + + v[CPU_METER_FREQUENCY] = -1; + return percent; } diff --git a/unsupported/Platform.c b/unsupported/Platform.c index ba844191..b5e50c3f 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -108,8 +108,11 @@ int Platform_getMaxPid() { } double Platform_setCPUValues(Meter* this, int cpu) { - (void) this; (void) cpu; + + double* v = this->values; + v[CPU_METER_FREQUENCY] = -1; + return 0.0; } From 81b64691a7ee55e5c665ac78290495b0aea946d7 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 10 Aug 2019 22:19:32 -0700 Subject: [PATCH 7/7] Move sysfs-reading code to LinuxProcessList.c and add average frequency. This way the frequency is read from sysfs only once per update cycle instead of every time the UI is redrawn. This also changes the code to read from /proc/cpuinfo instead. This is because reading from scaling_cur_freq stalls for 10ms if the previous read for the file was more than one second ago. [1] Since htop's update cycle is longer than that, it would cause the read of each CPU's scaling_cur_freq file to block the UI for 20ms. This easily led to a noticeable half-second lag on a 20+ CPU machine. /proc/cpuinfo also has a 10ms delay, but this applies for the whole file so the delay does not scale with the number of CPUs. [2] [1]: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=4815d3c56d1e10449a44089a47544d9ba84fad0d [2]: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=7d5905dc14a87805a59f3c5bf70173aac2bb18f8 --- CPUMeter.c | 10 ++---- linux/LinuxProcessList.c | 74 ++++++++++++++++++++++++++++++++++++++++ linux/LinuxProcessList.h | 6 ++++ linux/Platform.c | 15 +------- 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/CPUMeter.c b/CPUMeter.c index edcd61d2..6785410e 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -64,18 +64,14 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) { } memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT); double percent = Platform_setCPUValues(this, cpu); - if (cpu != 0 && this->pl->settings->showCPUFrequency) { - /* Initial frequency is in KHz. Divide it by 1000 till it's less than 1000, and emit unit accordingly */ + if (this->pl->settings->showCPUFrequency) { + /* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */ double cpuFrequency = this->values[CPU_METER_FREQUENCY]; - char unit = 'K'; + char unit = 'M'; char cpuFrequencyBuffer[16]; if (cpuFrequency < 0) { xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); } else { - if (cpuFrequency > 1000) { - cpuFrequency /= 1000; - unit = 'M'; - } if (cpuFrequency > 1000) { cpuFrequency /= 1000; unit = 'G'; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 5f38540c..39f8405f 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -75,6 +75,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -100,6 +102,10 @@ typedef struct LinuxProcessList_ { #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif @@ -1028,18 +1034,86 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) { cpuData->stealTime = steal; cpuData->guestTime = virtalltime; cpuData->totalTime = totaltime; + } double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file); return period; } +static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) { + ProcessList* pl = (ProcessList*) this; + Settings* settings = pl->settings; + + int cpus = this->super.cpuCount; + assert(cpus > 0); + + for (int i = 0; i <= cpus; i++) { + CPUData* cpuData = &(this->cpus[i]); + cpuData->frequency = -1; + } + + int numCPUsWithFrequency = 0; + double totalFrequency = 0; + + if (settings->showCPUFrequency) { + FILE* file = fopen(PROCCPUINFOFILE, "r"); + if (file == NULL) { + CRT_fatalError("Cannot open " PROCCPUINFOFILE); + } + + int cpuid = -1; + double frequency; + while (!feof(file)) { + char buffer[PROC_LINE_LENGTH]; + char *ok = fgets(buffer, PROC_LINE_LENGTH, file); + if (!ok) break; + + if ( + (sscanf(buffer, "processor : %d", &cpuid) == 1) || + (sscanf(buffer, "processor: %d", &cpuid) == 1) + ) { + if (cpuid < 0 || cpuid > (cpus - 1)) { + char buffer[64]; + xSnprintf(buffer, sizeof(buffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid); + CRT_fatalError(buffer); + } + } else if ( + (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || + (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) + ) { + if (cpuid < 0) { + CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line"); + } + + int cpu = cpuid + 1; + CPUData* cpuData = &(this->cpus[cpu]); + cpuData->frequency = frequency; + numCPUsWithFrequency++; + totalFrequency += frequency; + } else if (buffer[0] == '\n') { + cpuid = -1; + } + } + fclose(file); + + if (numCPUsWithFrequency > 0) { + this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency; + } + } + + double period = (double)this->cpus[0].totalPeriod / cpus; + return period; +} + void ProcessList_goThroughEntries(ProcessList* super) { LinuxProcessList* this = (LinuxProcessList*) super; LinuxProcessList_scanMemoryInfo(super); double period = LinuxProcessList_scanCPUTime(this); + LinuxProcessList_scanCPUFrequency(this); + struct timeval tv; gettimeofday(&tv, NULL); LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv); diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index f30b487d..ae36b354 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -48,6 +48,8 @@ typedef struct CPUData_ { unsigned long long int softIrqPeriod; unsigned long long int stealPeriod; unsigned long long int guestPeriod; + + double frequency; } CPUData; typedef struct TtyDriver_ { @@ -73,6 +75,10 @@ typedef struct LinuxProcessList_ { #define PROCDIR "/proc" #endif +#ifndef PROCCPUINFOFILE +#define PROCCPUINFOFILE PROCDIR "/cpuinfo" +#endif + #ifndef PROCSTATFILE #define PROCSTATFILE PROCDIR "/stat" #endif diff --git a/linux/Platform.c b/linux/Platform.c index fc81d3bf..806d4c00 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -193,20 +193,7 @@ double Platform_setCPUValues(Meter* this, int cpu) { percent = CLAMP(percent, 0.0, 100.0); if (isnan(percent)) percent = 0.0; - v[CPU_METER_FREQUENCY] = -1; - if (this->pl->settings->showCPUFrequency) { - char filename[64]; - xSnprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu - 1); - FILE* fd = fopen(filename, "r"); - if (fd) { - unsigned int cpuFrequency; - int n = fscanf(fd, "%u", &cpuFrequency); - fclose(fd); - if (n > 0) { - v[CPU_METER_FREQUENCY] = cpuFrequency; - } - } - } + v[CPU_METER_FREQUENCY] = cpuData->frequency; return percent; }