mirror of https://github.com/xzeldon/htop.git
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
This commit is contained in:
parent
909bb86f05
commit
81b64691a7
10
CPUMeter.c
10
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);
|
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
|
||||||
double percent = Platform_setCPUValues(this, cpu);
|
double percent = Platform_setCPUValues(this, cpu);
|
||||||
if (cpu != 0 && this->pl->settings->showCPUFrequency) {
|
if (this->pl->settings->showCPUFrequency) {
|
||||||
/* Initial frequency is in KHz. Divide it by 1000 till it's less than 1000, and emit unit accordingly */
|
/* Initial frequency is in MHz. Emit it as GHz if it's larger than 1000MHz */
|
||||||
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
|
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
|
||||||
char unit = 'K';
|
char unit = 'M';
|
||||||
char cpuFrequencyBuffer[16];
|
char cpuFrequencyBuffer[16];
|
||||||
if (cpuFrequency < 0) {
|
if (cpuFrequency < 0) {
|
||||||
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
|
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
|
||||||
} else {
|
} else {
|
||||||
if (cpuFrequency > 1000) {
|
|
||||||
cpuFrequency /= 1000;
|
|
||||||
unit = 'M';
|
|
||||||
}
|
|
||||||
if (cpuFrequency > 1000) {
|
if (cpuFrequency > 1000) {
|
||||||
cpuFrequency /= 1000;
|
cpuFrequency /= 1000;
|
||||||
unit = 'G';
|
unit = 'G';
|
||||||
|
|
|
@ -75,6 +75,8 @@ typedef struct CPUData_ {
|
||||||
unsigned long long int softIrqPeriod;
|
unsigned long long int softIrqPeriod;
|
||||||
unsigned long long int stealPeriod;
|
unsigned long long int stealPeriod;
|
||||||
unsigned long long int guestPeriod;
|
unsigned long long int guestPeriod;
|
||||||
|
|
||||||
|
double frequency;
|
||||||
} CPUData;
|
} CPUData;
|
||||||
|
|
||||||
typedef struct TtyDriver_ {
|
typedef struct TtyDriver_ {
|
||||||
|
@ -100,6 +102,10 @@ typedef struct LinuxProcessList_ {
|
||||||
#define PROCDIR "/proc"
|
#define PROCDIR "/proc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef PROCCPUINFOFILE
|
||||||
|
#define PROCCPUINFOFILE PROCDIR "/cpuinfo"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef PROCSTATFILE
|
#ifndef PROCSTATFILE
|
||||||
#define PROCSTATFILE PROCDIR "/stat"
|
#define PROCSTATFILE PROCDIR "/stat"
|
||||||
#endif
|
#endif
|
||||||
|
@ -1028,18 +1034,86 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
|
||||||
cpuData->stealTime = steal;
|
cpuData->stealTime = steal;
|
||||||
cpuData->guestTime = virtalltime;
|
cpuData->guestTime = virtalltime;
|
||||||
cpuData->totalTime = totaltime;
|
cpuData->totalTime = totaltime;
|
||||||
|
|
||||||
}
|
}
|
||||||
double period = (double)this->cpus[0].totalPeriod / cpus;
|
double period = (double)this->cpus[0].totalPeriod / cpus;
|
||||||
fclose(file);
|
fclose(file);
|
||||||
return period;
|
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) {
|
void ProcessList_goThroughEntries(ProcessList* super) {
|
||||||
LinuxProcessList* this = (LinuxProcessList*) super;
|
LinuxProcessList* this = (LinuxProcessList*) super;
|
||||||
|
|
||||||
LinuxProcessList_scanMemoryInfo(super);
|
LinuxProcessList_scanMemoryInfo(super);
|
||||||
double period = LinuxProcessList_scanCPUTime(this);
|
double period = LinuxProcessList_scanCPUTime(this);
|
||||||
|
|
||||||
|
LinuxProcessList_scanCPUFrequency(this);
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
|
LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
|
||||||
|
|
|
@ -48,6 +48,8 @@ typedef struct CPUData_ {
|
||||||
unsigned long long int softIrqPeriod;
|
unsigned long long int softIrqPeriod;
|
||||||
unsigned long long int stealPeriod;
|
unsigned long long int stealPeriod;
|
||||||
unsigned long long int guestPeriod;
|
unsigned long long int guestPeriod;
|
||||||
|
|
||||||
|
double frequency;
|
||||||
} CPUData;
|
} CPUData;
|
||||||
|
|
||||||
typedef struct TtyDriver_ {
|
typedef struct TtyDriver_ {
|
||||||
|
@ -73,6 +75,10 @@ typedef struct LinuxProcessList_ {
|
||||||
#define PROCDIR "/proc"
|
#define PROCDIR "/proc"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef PROCCPUINFOFILE
|
||||||
|
#define PROCCPUINFOFILE PROCDIR "/cpuinfo"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef PROCSTATFILE
|
#ifndef PROCSTATFILE
|
||||||
#define PROCSTATFILE PROCDIR "/stat"
|
#define PROCSTATFILE PROCDIR "/stat"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -193,20 +193,7 @@ double Platform_setCPUValues(Meter* this, int cpu) {
|
||||||
percent = CLAMP(percent, 0.0, 100.0);
|
percent = CLAMP(percent, 0.0, 100.0);
|
||||||
if (isnan(percent)) percent = 0.0;
|
if (isnan(percent)) percent = 0.0;
|
||||||
|
|
||||||
v[CPU_METER_FREQUENCY] = -1;
|
v[CPU_METER_FREQUENCY] = cpuData->frequency;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return percent;
|
return percent;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue