linux: improve /proc/cpuinfo parser
Make uv_cpu_info() understand the ARM and MIPS versions of /proc/cpuinfo,
it only knew how to deal with the x86 version
This commit also fixes a buglet where uv_cpu_info() reported the maximum CPU
frequency instead of the actual CPU frequency. That is, before this commit
`out/Debug/run-tests platform_output | grep speed | sort | uniq -c` on my
system always reported:
8 speed: 3400
Now it reports (for example):
2 speed: 3400
6 speed: 1600
In other words, two CPUs are running at full speed while the others have been
scaled back because they're mostly idle.
Fixes #526.
This commit is contained in:
parent
f4f294f7c6
commit
54bfb66806
@ -63,6 +63,11 @@ static struct {
|
|||||||
size_t len;
|
size_t len;
|
||||||
} process_title;
|
} process_title;
|
||||||
|
|
||||||
|
static void read_models(unsigned int numcpus, uv_cpu_info_t* ci);
|
||||||
|
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
|
||||||
|
static void read_times(unsigned int numcpus, uv_cpu_info_t* ci);
|
||||||
|
static unsigned long read_cpufreq(unsigned int cpunum);
|
||||||
|
|
||||||
|
|
||||||
uint64_t uv_hrtime() {
|
uint64_t uv_hrtime() {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
@ -297,84 +302,166 @@ uv_err_t uv_uptime(double* uptime) {
|
|||||||
|
|
||||||
|
|
||||||
uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||||
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
|
unsigned int numcpus;
|
||||||
multiplier = ((uint64_t)1000L / ticks), cpuspeed;
|
uv_cpu_info_t* ci;
|
||||||
int numcpus = 0, i = 0;
|
|
||||||
unsigned long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
|
|
||||||
char line[512], speedPath[256], model[512];
|
|
||||||
FILE *fpStat = fopen("/proc/stat", "r");
|
|
||||||
FILE *fpModel = fopen("/proc/cpuinfo", "r");
|
|
||||||
FILE *fpSpeed;
|
|
||||||
uv_cpu_info_t* cpu_info;
|
|
||||||
|
|
||||||
if (fpModel) {
|
*cpu_infos = NULL;
|
||||||
while (fgets(line, 511, fpModel) != NULL) {
|
*count = 0;
|
||||||
if (strncmp(line, "model name", 10) == 0) {
|
|
||||||
numcpus++;
|
|
||||||
if (numcpus == 1) {
|
|
||||||
char *p = strchr(line, ':') + 2;
|
|
||||||
strcpy(model, p);
|
|
||||||
model[strlen(model)-1] = 0;
|
|
||||||
}
|
|
||||||
} else if (strncmp(line, "cpu MHz", 7) == 0) {
|
|
||||||
if (numcpus == 1) {
|
|
||||||
sscanf(line, "%*s %*s : %u", &cpuspeed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fpModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
*cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t));
|
numcpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
if (!(*cpu_infos)) {
|
assert(numcpus != (unsigned int) -1);
|
||||||
return uv__new_artificial_error(UV_ENOMEM);
|
assert(numcpus != 0);
|
||||||
}
|
|
||||||
|
|
||||||
|
ci = calloc(numcpus, sizeof(*ci));
|
||||||
|
if (ci == NULL)
|
||||||
|
return uv__new_sys_error(ENOMEM);
|
||||||
|
|
||||||
|
read_speeds(numcpus, ci);
|
||||||
|
read_models(numcpus, ci);
|
||||||
|
read_times(numcpus, ci);
|
||||||
|
|
||||||
|
*cpu_infos = ci;
|
||||||
*count = numcpus;
|
*count = numcpus;
|
||||||
|
|
||||||
cpu_info = *cpu_infos;
|
|
||||||
|
|
||||||
if (fpStat) {
|
|
||||||
while (fgets(line, 511, fpStat) != NULL) {
|
|
||||||
if (strncmp(line, "cpu ", 4) == 0) {
|
|
||||||
continue;
|
|
||||||
} else if (strncmp(line, "cpu", 3) != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sscanf(line, "%*s %lu %lu %lu %lu %*s %lu",
|
|
||||||
&ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
|
|
||||||
snprintf(speedPath, sizeof(speedPath),
|
|
||||||
"/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
|
|
||||||
|
|
||||||
fpSpeed = fopen(speedPath, "r");
|
|
||||||
|
|
||||||
if (fpSpeed) {
|
|
||||||
if (fgets(line, 511, fpSpeed) != NULL) {
|
|
||||||
sscanf(line, "%u", &cpuspeed);
|
|
||||||
cpuspeed /= 1000;
|
|
||||||
}
|
|
||||||
fclose(fpSpeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_info->cpu_times.user = ticks_user * multiplier;
|
|
||||||
cpu_info->cpu_times.nice = ticks_nice * multiplier;
|
|
||||||
cpu_info->cpu_times.sys = ticks_sys * multiplier;
|
|
||||||
cpu_info->cpu_times.idle = ticks_idle * multiplier;
|
|
||||||
cpu_info->cpu_times.irq = ticks_intr * multiplier;
|
|
||||||
|
|
||||||
cpu_info->model = strdup(model);
|
|
||||||
cpu_info->speed = cpuspeed;
|
|
||||||
|
|
||||||
cpu_info++;
|
|
||||||
}
|
|
||||||
fclose(fpStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
return uv_ok_;
|
return uv_ok_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) {
|
||||||
|
unsigned int num;
|
||||||
|
|
||||||
|
for (num = 0; num < numcpus; num++)
|
||||||
|
ci[num].speed = read_cpufreq(num) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
static const char marker[] = "model name\t: ";
|
||||||
|
#elif defined(__arm__)
|
||||||
|
static const char marker[] = "Processor\t: ";
|
||||||
|
#elif defined(__mips__)
|
||||||
|
static const char marker[] = "cpu model\t\t: ";
|
||||||
|
#else
|
||||||
|
# warning uv_cpu_info() is not supported on this architecture.
|
||||||
|
static const char marker[] = "(dummy)";
|
||||||
|
#endif
|
||||||
|
unsigned int num;
|
||||||
|
char buf[1024];
|
||||||
|
char* model;
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
fp = fopen("/proc/cpuinfo", "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
num = 0;
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
if (num >= numcpus)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strncmp(buf, marker, sizeof(marker) - 1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
model = buf + sizeof(marker) - 1;
|
||||||
|
model = strndup(model, strlen(model) - 1); /* strip newline */
|
||||||
|
ci[num++].model = model;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void read_times(unsigned int numcpus, uv_cpu_info_t* ci) {
|
||||||
|
unsigned long clock_ticks;
|
||||||
|
struct uv_cpu_times_s ts;
|
||||||
|
unsigned long user;
|
||||||
|
unsigned long nice;
|
||||||
|
unsigned long sys;
|
||||||
|
unsigned long idle;
|
||||||
|
unsigned long dummy;
|
||||||
|
unsigned long irq;
|
||||||
|
unsigned int num;
|
||||||
|
unsigned int len;
|
||||||
|
char buf[1024];
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
clock_ticks = sysconf(_SC_CLK_TCK);
|
||||||
|
assert(clock_ticks != (unsigned long) -1);
|
||||||
|
assert(clock_ticks != 0);
|
||||||
|
|
||||||
|
fp = fopen("/proc/stat", "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!fgets(buf, sizeof(buf), fp))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
num = 0;
|
||||||
|
|
||||||
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
|
if (num >= numcpus)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strncmp(buf, "cpu", 3))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* skip "cpu<num> " marker */
|
||||||
|
{
|
||||||
|
unsigned int n = num;
|
||||||
|
for (len = sizeof("cpu0"); n /= 10; len++);
|
||||||
|
assert(sscanf(buf, "cpu%u ", &n) == 1 && n == num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Line contains user, nice, system, idle, iowait, irq, softirq, steal,
|
||||||
|
* guest, guest_nice but we're only interested in the first four + irq.
|
||||||
|
*
|
||||||
|
* Don't use %*s to skip fields or %ll to read straight into the uint64_t
|
||||||
|
* fields, they're not allowed in C89 mode.
|
||||||
|
*/
|
||||||
|
if (6 != sscanf(buf + len,
|
||||||
|
"%lu %lu %lu %lu %lu %lu",
|
||||||
|
&user,
|
||||||
|
&nice,
|
||||||
|
&sys,
|
||||||
|
&idle,
|
||||||
|
&dummy,
|
||||||
|
&irq))
|
||||||
|
abort();
|
||||||
|
|
||||||
|
ts.user = clock_ticks * user;
|
||||||
|
ts.nice = clock_ticks * nice;
|
||||||
|
ts.sys = clock_ticks * sys;
|
||||||
|
ts.idle = clock_ticks * idle;
|
||||||
|
ts.irq = clock_ticks * irq;
|
||||||
|
ci[num++].cpu_times = ts;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned long read_cpufreq(unsigned int cpunum) {
|
||||||
|
unsigned long val;
|
||||||
|
char buf[1024];
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
snprintf(buf,
|
||||||
|
sizeof(buf),
|
||||||
|
"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq",
|
||||||
|
cpunum);
|
||||||
|
|
||||||
|
fp = fopen(buf, "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
val = 0;
|
||||||
|
fscanf(fp, "%lu", &val);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user