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; }