Multiple CPU support for cpu_usage (#209)
This change addresses the issue #199 asking for multiple CPU support. It takes an arbitrary CPU number and outputs its usage using the same arithmetics as for CPU aggregation. It currently doesn't support FreeBSD.
This commit is contained in:
parent
3ae0decbb3
commit
94651257ce
@ -422,6 +422,7 @@ int main(int argc, char *argv[]) {
|
||||
CFG_STR("format", "%usage", CFGF_NONE),
|
||||
CFG_STR("format_above_threshold", NULL, CFGF_NONE),
|
||||
CFG_STR("format_above_degraded_threshold", NULL, CFGF_NONE),
|
||||
CFG_STR("path", "/proc/stat", CFGF_NONE),
|
||||
CFG_FLOAT("max_threshold", 95, CFGF_NONE),
|
||||
CFG_FLOAT("degraded_threshold", 90, CFGF_NONE),
|
||||
CFG_CUSTOM_ALIGN_OPT,
|
||||
@ -751,7 +752,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
CASE_SEC("cpu_usage") {
|
||||
SEC_OPEN_MAP("cpu_usage");
|
||||
print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_above_threshold"), cfg_getstr(sec, "format_above_degraded_threshold"), cfg_getfloat(sec, "max_threshold"), cfg_getfloat(sec, "degraded_threshold"));
|
||||
print_cpu_usage(json_gen, buffer, cfg_getstr(sec, "format"), cfg_getstr(sec, "format_above_threshold"), cfg_getstr(sec, "format_above_degraded_threshold"), cfg_getstr(sec, "path"), cfg_getfloat(sec, "max_threshold"), cfg_getfloat(sec, "degraded_threshold"));
|
||||
SEC_CLOSE_MAP;
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ void print_wireless_info(yajl_gen json_gen, char *buffer, const char *interface,
|
||||
void print_run_watch(yajl_gen json_gen, char *buffer, const char *title, const char *pidfile, const char *format, const char *format_down);
|
||||
void print_path_exists(yajl_gen json_gen, char *buffer, const char *title, const char *path, const char *format, const char *format_down);
|
||||
void print_cpu_temperature_info(yajl_gen json_gen, char *buffer, int zone, const char *path, const char *format, const char *format_above_threshold, int);
|
||||
void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const float max_threshold, const float degraded_threshold);
|
||||
void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold);
|
||||
void print_eth_info(yajl_gen json_gen, char *buffer, const char *interface, const char *format_up, const char *format_down);
|
||||
void print_load(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const float max_threshold);
|
||||
void print_volume(yajl_gen json_gen, char *buffer, const char *fmt, const char *fmt_muted, const char *device, const char *mixer, int mixer_idx);
|
||||
|
@ -405,13 +405,16 @@ format_above_threshold.
|
||||
|
||||
It is possible to define a degraded_threshold that will color the load
|
||||
value yellow in case the CPU average over the last interval is getting
|
||||
higher than the configured threshold. Defaults to 90. The output format
|
||||
higher than the configured threshold. Defaults to 90. The output format
|
||||
when above degraded threshold can be customized with
|
||||
format_above_degraded_threshold.
|
||||
|
||||
For displaying the Nth CPU usage, you can use the %cpu<N> format string,
|
||||
starting from %cpu0. This feature is currently not supported in FreeBSD.
|
||||
|
||||
*Example order*: +cpu_usage+
|
||||
|
||||
*Example format*: +%usage+
|
||||
*Example format*: +all: %usage CPU_0: %cpu0 CPU_1: %cpu1+
|
||||
|
||||
*Example max_threshold*: +75+
|
||||
|
||||
|
@ -33,36 +33,68 @@
|
||||
|
||||
#include "i3status.h"
|
||||
|
||||
static int prev_total = 0;
|
||||
static int prev_idle = 0;
|
||||
struct cpu_usage {
|
||||
int user;
|
||||
int nice;
|
||||
int system;
|
||||
int idle;
|
||||
int total;
|
||||
};
|
||||
|
||||
static int cpu_count = 0;
|
||||
static struct cpu_usage prev_all = {0, 0, 0, 0, 0};
|
||||
static struct cpu_usage *prev_cpus = NULL;
|
||||
static struct cpu_usage *curr_cpus = NULL;
|
||||
|
||||
/*
|
||||
* Reads the CPU utilization from /proc/stat and returns the usage as a
|
||||
* percentage.
|
||||
*
|
||||
*/
|
||||
void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const float max_threshold, const float degraded_threshold) {
|
||||
void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const char *format_above_threshold, const char *format_above_degraded_threshold, const char *path, const float max_threshold, const float degraded_threshold) {
|
||||
const char *selected_format = format;
|
||||
const char *walk;
|
||||
char *outwalk = buffer;
|
||||
int curr_user = 0, curr_nice = 0, curr_system = 0, curr_idle = 0, curr_total;
|
||||
struct cpu_usage curr_all = {0, 0, 0, 0, 0};
|
||||
int diff_idle, diff_total, diff_usage;
|
||||
bool colorful_output = false;
|
||||
|
||||
#if defined(LINUX)
|
||||
static char statpath[512];
|
||||
char buf[1024];
|
||||
strcpy(statpath, "/proc/stat");
|
||||
if (!slurp(statpath, buf, sizeof(buf)) ||
|
||||
sscanf(buf, "cpu %d %d %d %d", &curr_user, &curr_nice, &curr_system, &curr_idle) != 4)
|
||||
goto error;
|
||||
|
||||
curr_total = curr_user + curr_nice + curr_system + curr_idle;
|
||||
diff_idle = curr_idle - prev_idle;
|
||||
diff_total = curr_total - prev_total;
|
||||
// Detecting if CPU count has changed
|
||||
int curr_cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (curr_cpu_count != cpu_count) {
|
||||
cpu_count = curr_cpu_count;
|
||||
free(prev_cpus);
|
||||
prev_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage));
|
||||
free(curr_cpus);
|
||||
curr_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage));
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
if (!slurp(path, buf, sizeof(buf)))
|
||||
goto error;
|
||||
// Parsing all cpu values using strtok
|
||||
if (strtok(buf, "\n") == NULL)
|
||||
goto error;
|
||||
char *buf_itr = NULL;
|
||||
for (int cpu_idx = 0; cpu_idx < cpu_count; cpu_idx++) {
|
||||
buf_itr = strtok(NULL, "\n");
|
||||
int curr_cpu_idx = -1;
|
||||
if (!buf_itr || sscanf(buf_itr, "cpu%d %d %d %d %d", &curr_cpu_idx, &curr_cpus[cpu_idx].user, &curr_cpus[cpu_idx].nice, &curr_cpus[cpu_idx].system, &curr_cpus[cpu_idx].idle) != 5 || curr_cpu_idx != cpu_idx)
|
||||
goto error;
|
||||
curr_cpus[cpu_idx].total = curr_cpus[cpu_idx].user + curr_cpus[cpu_idx].nice + curr_cpus[cpu_idx].system + curr_cpus[cpu_idx].idle;
|
||||
curr_all.user += curr_cpus[cpu_idx].user;
|
||||
curr_all.nice += curr_cpus[cpu_idx].nice;
|
||||
curr_all.system += curr_cpus[cpu_idx].system;
|
||||
curr_all.idle += curr_cpus[cpu_idx].idle;
|
||||
curr_all.total += curr_cpus[cpu_idx].total;
|
||||
}
|
||||
|
||||
diff_idle = curr_all.idle - prev_all.idle;
|
||||
diff_total = curr_all.total - prev_all.total;
|
||||
diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0);
|
||||
prev_total = curr_total;
|
||||
prev_idle = curr_idle;
|
||||
prev_all = curr_all;
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
@ -84,16 +116,15 @@ void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
curr_user = cp_time[CP_USER];
|
||||
curr_nice = cp_time[CP_NICE];
|
||||
curr_system = cp_time[CP_SYS];
|
||||
curr_idle = cp_time[CP_IDLE];
|
||||
curr_total = curr_user + curr_nice + curr_system + curr_idle;
|
||||
diff_idle = curr_idle - prev_idle;
|
||||
diff_total = curr_total - prev_total;
|
||||
curr_all.user = cp_time[CP_USER];
|
||||
curr_all.nice = cp_time[CP_NICE];
|
||||
curr_all.system = cp_time[CP_SYS];
|
||||
curr_all.idle = cp_time[CP_IDLE];
|
||||
curr_all.total = curr_all.user + curr_all.nice + curr_all.system + curr_all.idle;
|
||||
diff_idle = curr_all.idle - prev_all.idle;
|
||||
diff_total = curr_all.total - prev_all.total;
|
||||
diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0);
|
||||
prev_total = curr_total;
|
||||
prev_idle = curr_idle;
|
||||
prev_all = curr_all;
|
||||
#else
|
||||
goto error;
|
||||
#endif
|
||||
@ -120,8 +151,32 @@ void print_cpu_usage(yajl_gen json_gen, char *buffer, const char *format, const
|
||||
outwalk += sprintf(outwalk, "%02d%s", diff_usage, pct_mark);
|
||||
walk += strlen("usage");
|
||||
}
|
||||
#if defined(LINUX)
|
||||
if (BEGINS_WITH(walk + 1, "cpu")) {
|
||||
int number = 0;
|
||||
sscanf(walk + 1, "cpu%d", &number);
|
||||
if (number < 0 || number >= cpu_count) {
|
||||
fprintf(stderr, "provided CPU number '%d' above detected number of CPU %d\n", number, cpu_count);
|
||||
} else {
|
||||
int cpu_diff_idle = curr_cpus[number].idle - prev_cpus[number].idle;
|
||||
int cpu_diff_total = curr_cpus[number].total - prev_cpus[number].total;
|
||||
int cpu_diff_usage = (cpu_diff_total ? (1000 * (cpu_diff_total - cpu_diff_idle) / cpu_diff_total + 5) / 10 : 0);
|
||||
outwalk += sprintf(outwalk, "%02d%s", cpu_diff_usage, pct_mark);
|
||||
}
|
||||
int padding = 1;
|
||||
int step = 10;
|
||||
while (step < number) {
|
||||
step *= 10;
|
||||
padding++;
|
||||
}
|
||||
walk += strlen("cpu") + padding;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int i = 0; i < cpu_count; i++)
|
||||
prev_cpus[i] = curr_cpus[i];
|
||||
|
||||
if (colorful_output)
|
||||
END_COLOR;
|
||||
|
||||
|
1
testcases/010-cpu-usage/expected_output.txt
Normal file
1
testcases/010-cpu-usage/expected_output.txt
Normal file
@ -0,0 +1 @@
|
||||
all: 75% CPU_0: 100% CPU_1: 50%
|
12
testcases/010-cpu-usage/i3status.conf
Normal file
12
testcases/010-cpu-usage/i3status.conf
Normal file
@ -0,0 +1,12 @@
|
||||
general {
|
||||
output_format = "none"
|
||||
}
|
||||
|
||||
order += "cpu_usage"
|
||||
|
||||
cpu_usage {
|
||||
format = "all: %usage CPU_0: %cpu0 CPU_1: %cpu1"
|
||||
path = "testcases/010-cpu-usage/stat"
|
||||
max_threshold = 90
|
||||
degraded_threshold = 75
|
||||
}
|
3
testcases/010-cpu-usage/stat
Normal file
3
testcases/010-cpu-usage/stat
Normal file
@ -0,0 +1,3 @@
|
||||
cpu 0 0 0 0 0 0 0 0 0 0
|
||||
cpu0 100 0 0 0 0 0 0 0 0 0
|
||||
cpu1 50 0 0 50 0 0 0 0 0 0
|
1
testcases/011-cpu-usage/expected_output.txt
Normal file
1
testcases/011-cpu-usage/expected_output.txt
Normal file
@ -0,0 +1 @@
|
||||
all: 50% CPU_0: 00% CPU_1: 100%
|
12
testcases/011-cpu-usage/i3status.conf
Normal file
12
testcases/011-cpu-usage/i3status.conf
Normal file
@ -0,0 +1,12 @@
|
||||
general {
|
||||
output_format = "none"
|
||||
}
|
||||
|
||||
order += "cpu_usage"
|
||||
|
||||
cpu_usage {
|
||||
format = "all: %usage CPU_0: %cpu0 CPU_1: %cpu1"
|
||||
path = "testcases/011-cpu-usage/stat"
|
||||
max_threshold = 90
|
||||
degraded_threshold = 75
|
||||
}
|
3
testcases/011-cpu-usage/stat
Normal file
3
testcases/011-cpu-usage/stat
Normal file
@ -0,0 +1,3 @@
|
||||
cpu 0 0 0 0 0 0 0 0 0 0
|
||||
cpu0 0 0 0 300 0 0 0 0 0 0
|
||||
cpu1 100 100 100 0 0 0 0 0 0 0
|
1
testcases/012-cpu-usage-error/expected_output.txt
Normal file
1
testcases/012-cpu-usage-error/expected_output.txt
Normal file
@ -0,0 +1 @@
|
||||
cant read cpu usage
|
12
testcases/012-cpu-usage-error/i3status.conf
Normal file
12
testcases/012-cpu-usage-error/i3status.conf
Normal file
@ -0,0 +1,12 @@
|
||||
general {
|
||||
output_format = "none"
|
||||
}
|
||||
|
||||
order += "cpu_usage"
|
||||
|
||||
cpu_usage {
|
||||
format = "all: %usage CPU_0: %cpu0 CPU_1: %cpu1"
|
||||
path = "testcases/012-cpu-usage-error/stat"
|
||||
max_threshold = 90
|
||||
degraded_threshold = 75
|
||||
}
|
2
testcases/012-cpu-usage-error/stat
Normal file
2
testcases/012-cpu-usage-error/stat
Normal file
@ -0,0 +1,2 @@
|
||||
cpu 0 0 0 0 0 0 0 0 0 0
|
||||
cpu0 100 0 0 0 0 0 0 0 0 0
|
1
testcases/013-cpu-usage-error/expected_output.txt
Normal file
1
testcases/013-cpu-usage-error/expected_output.txt
Normal file
@ -0,0 +1 @@
|
||||
cant read cpu usage
|
12
testcases/013-cpu-usage-error/i3status.conf
Normal file
12
testcases/013-cpu-usage-error/i3status.conf
Normal file
@ -0,0 +1,12 @@
|
||||
general {
|
||||
output_format = "none"
|
||||
}
|
||||
|
||||
order += "cpu_usage"
|
||||
|
||||
cpu_usage {
|
||||
format = "all: %usage CPU_0: %cpu0 CPU_1: %cpu1"
|
||||
path = "testcases/013-cpu-usage-error/stat"
|
||||
max_threshold = 90
|
||||
degraded_threshold = 75
|
||||
}
|
2
testcases/013-cpu-usage-error/stat
Normal file
2
testcases/013-cpu-usage-error/stat
Normal file
@ -0,0 +1,2 @@
|
||||
cpu0 100 0 0 0 0 0 0 0 0 0
|
||||
cpu1 50 0 0 50 0 0 0 0 0 0
|
Loading…
Reference in New Issue
Block a user