Read CPU frequency from sysfs by default

Use the more portable sysfs node /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq
to get the CPU frequency.
In case of an error fall back to /proc/cpuinfo .

Also use a fixed width of 4 for the frequency to avoid position jumps
in case the frequency moves in the range 900-1100 MHz.
This commit is contained in:
Christian Göttsche 2020-09-22 14:50:50 +02:00 committed by cgzones
parent f4e1f4619f
commit edf1b10d2c
2 changed files with 85 additions and 59 deletions

View File

@ -45,7 +45,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
if (isnan(cpuFrequency)) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.0fMHz", cpuFrequency);
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency);
}
if (this->pl->settings->showCPUUsage) {
xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);

View File

@ -1145,53 +1145,69 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
return period;
}
static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
ProcessList* pl = (ProcessList*) this;
Settings* settings = pl->settings;
static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) {
int cpus = this->super.cpuCount;
int numCPUsWithFrequency = 0;
unsigned long totalFrequency = 0;
for (int i = 0; i < cpus; ++i) {
char pathBuffer[64];
xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
FILE* file = fopen(pathBuffer, "r");
if (!file)
return -errno;
unsigned long frequency;
if (fscanf(file, "%lu", &frequency) == 1) {
/* convert kHz to MHz */
frequency = frequency / 1000;
this->cpus[i + 1].frequency = frequency;
numCPUsWithFrequency++;
totalFrequency += frequency;
}
fclose(file);
}
if (numCPUsWithFrequency > 0)
this->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
return 0;
}
static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
FILE* file = fopen(PROCCPUINFOFILE, "r");
if (file == NULL)
return;
int cpus = this->super.cpuCount;
assert(cpus > 0);
for (int i = 0; i <= cpus; i++) {
CPUData* cpuData = &(this->cpus[i]);
cpuData->frequency = NAN;
}
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)) {
double frequency;
char buffer[PROC_LINE_LENGTH];
char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
if (!ok) break;
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
break;
if (
(sscanf(buffer, "processor : %d", &cpuid) == 1) ||
(sscanf(buffer, "processor: %d", &cpuid) == 1)
) {
if (cpuid < 0 || cpuid > (cpus - 1)) {
char tmpbuffer[64];
xSnprintf(tmpbuffer, sizeof(tmpbuffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid);
CRT_fatalError(tmpbuffer);
}
continue;
} else if (
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
) {
if (cpuid < 0 || cpuid > (cpus - 1)) {
CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line");
}
if (cpuid < 0 || cpuid > (cpus - 1))
continue;
int cpu = cpuid + 1;
CPUData* cpuData = &(this->cpus[cpu]);
CPUData* cpuData = &(this->cpus[cpuid + 1]);
/* do not override sysfs data */
if (isnan(cpuData->frequency))
cpuData->frequency = frequency;
numCPUsWithFrequency++;
totalFrequency += frequency;
@ -1201,22 +1217,32 @@ static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
}
fclose(file);
if (numCPUsWithFrequency > 0) {
if (numCPUsWithFrequency > 0)
this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
}
}
double period = (double)this->cpus[0].totalPeriod / cpus;
return period;
static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
int cpus = this->super.cpuCount;
assert(cpus > 0);
for (int i = 0; i <= cpus; i++)
this->cpus[i].frequency = NAN;
if (scanCPUFreqencyFromSysCPUFreq(this) == 0)
return;
scanCPUFreqencyFromCPUinfo(this);
}
void ProcessList_goThroughEntries(ProcessList* super) {
LinuxProcessList* this = (LinuxProcessList*) super;
const Settings* settings = super->settings;
LinuxProcessList_scanMemoryInfo(super);
LinuxProcessList_scanZfsArcstats(this);
double period = LinuxProcessList_scanCPUTime(this);
if (settings->showCPUFrequency)
LinuxProcessList_scanCPUFrequency(this);
struct timeval tv;