diff --git a/CPUMeter.c b/CPUMeter.c index 8a23c487..badcedab 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,30 @@ 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 (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 = 'M'; + char cpuFrequencyBuffer[16]; + if (cpuFrequency < 0) { + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A"); + } else { + if (cpuFrequency > 1000) { + cpuFrequency /= 1000; + unit = 'G'; + } + xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.3f%cHz", cpuFrequency, unit); + } + if (this->pl->settings->showCPUUsage) { + xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer); + } else { + xSnprintf(buffer, size, "%s", cpuFrequencyBuffer); + } + } else if (this->pl->settings->showCPUUsage) { + xSnprintf(buffer, size, "%5.1f%%", percent); + } else if (size > 0) { + buffer[0] = '\0'; + } } 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 ece4c7c5..0f44acc2 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -97,6 +97,8 @@ 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))); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse))); return this; } diff --git a/Settings.c b/Settings.c index 69a94a8b..116595b4 100644 --- a/Settings.c +++ b/Settings.c @@ -45,6 +45,8 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; @@ -224,6 +226,10 @@ 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")) { this->updateProcessNames = atoi(option[1]); } else if (String_eq(option[0], "account_guest_in_cpu_meter")) { @@ -315,6 +321,8 @@ 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); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); @@ -344,6 +352,8 @@ 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; this->showProgramPath = true; diff --git a/Settings.h b/Settings.h index b9bcf87b..cf8217b5 100644 --- a/Settings.h +++ b/Settings.h @@ -36,6 +36,8 @@ typedef struct Settings_ { bool countCPUsFromZero; bool detailedCPUTime; + bool showCPUUsage; + bool showCPUFrequency; bool treeView; bool showProgramPath; bool hideThreads; diff --git a/darwin/Platform.c b/darwin/Platform.c index 27a7b850..a052ea8a 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/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 7717d5a4..5cbaee6a 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -74,6 +74,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 @@ -1108,18 +1114,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 4e0acc80..24b94d87 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -47,6 +47,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 130403f2..afdd3f02 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -198,6 +198,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] = cpuData->frequency; + return percent; } diff --git a/linux/Platform.h b/linux/Platform.h index 14eafbfc..bb2c4b09 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -47,6 +47,4 @@ extern char* Platform_getProcessEnv(pid_t pid); extern void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); -void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); - #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 343f4e79..0f5279e8 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -170,6 +170,7 @@ double Platform_setCPUValues(Meter* this, int cpu) { v[CPU_METER_STEAL] = 0.0; v[CPU_METER_GUEST] = 0.0; v[CPU_METER_IOWAIT] = 0.0; + v[CPU_METER_FREQUENCY] = -1; Meter_setItems(this, 8); totalPercent = v[0]+v[1]+v[2]+v[3]; } else { diff --git a/solaris/Platform.c b/solaris/Platform.c index 396b502c..1322598e 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 e4c0e8ce..0e46a348 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; }