summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreplanet <emeric.planet@gmail.com>2017-03-26 12:54:07 +0200
committerMichael Stapelberg <stapelberg@users.noreply.github.com>2017-03-26 06:54:07 -0400
commit94651257cea4ff419f9cf2143d93193d5a5ccb96 (patch)
treeca563d7570df6c8e84e93a04ecf85037ab77eae8
parent3ae0decbb33015fb111cdf7ea3d117bd6988cc05 (diff)
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.
-rw-r--r--i3status.c3
-rw-r--r--include/i3status.h2
-rw-r--r--man/i3status.man7
-rw-r--r--src/print_cpu_usage.c101
-rw-r--r--testcases/010-cpu-usage/expected_output.txt1
-rw-r--r--testcases/010-cpu-usage/i3status.conf12
-rw-r--r--testcases/010-cpu-usage/stat3
-rw-r--r--testcases/011-cpu-usage/expected_output.txt1
-rw-r--r--testcases/011-cpu-usage/i3status.conf12
-rw-r--r--testcases/011-cpu-usage/stat3
-rw-r--r--testcases/012-cpu-usage-error/expected_output.txt1
-rw-r--r--testcases/012-cpu-usage-error/i3status.conf12
-rw-r--r--testcases/012-cpu-usage-error/stat2
-rw-r--r--testcases/013-cpu-usage-error/expected_output.txt1
-rw-r--r--testcases/013-cpu-usage-error/i3status.conf12
-rw-r--r--testcases/013-cpu-usage-error/stat2
16 files changed, 148 insertions, 27 deletions
diff --git a/i3status.c b/i3status.c
index 3b9ab72..558f8ab 100644
--- a/i3status.c
+++ b/i3status.c
@@ -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;
}
}
diff --git a/include/i3status.h b/include/i3status.h
index a0e1e31..5042832 100644
--- a/include/i3status.h
+++ b/include/i3status.h
@@ -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);
diff --git a/man/i3status.man b/man/i3status.man
index 22d51ca..f1b6221 100644
--- a/man/i3status.man
+++ b/man/i3status.man
@@ -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+
diff --git a/src/print_cpu_usage.c b/src/print_cpu_usage.c
index 45a5ef2..c1ea3fd 100644
--- a/src/print_cpu_usage.c
+++ b/src/print_cpu_usage.c
@@ -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)
+
+ // 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;
+ }
- curr_total = curr_user + curr_nice + curr_system + curr_idle;
- diff_idle = curr_idle - prev_idle;
- diff_total = curr_total - prev_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;
diff --git a/testcases/010-cpu-usage/expected_output.txt b/testcases/010-cpu-usage/expected_output.txt
new file mode 100644
index 0000000..336596e
--- /dev/null
+++ b/testcases/010-cpu-usage/expected_output.txt
@@ -0,0 +1 @@
+all: 75% CPU_0: 100% CPU_1: 50%
diff --git a/testcases/010-cpu-usage/i3status.conf b/testcases/010-cpu-usage/i3status.conf
new file mode 100644
index 0000000..57cddf2
--- /dev/null
+++ b/testcases/010-cpu-usage/i3status.conf
@@ -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
+}
diff --git a/testcases/010-cpu-usage/stat b/testcases/010-cpu-usage/stat
new file mode 100644
index 0000000..6fbc94e
--- /dev/null
+++ b/testcases/010-cpu-usage/stat
@@ -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
diff --git a/testcases/011-cpu-usage/expected_output.txt b/testcases/011-cpu-usage/expected_output.txt
new file mode 100644
index 0000000..930a2b5
--- /dev/null
+++ b/testcases/011-cpu-usage/expected_output.txt
@@ -0,0 +1 @@
+all: 50% CPU_0: 00% CPU_1: 100%
diff --git a/testcases/011-cpu-usage/i3status.conf b/testcases/011-cpu-usage/i3status.conf
new file mode 100644
index 0000000..39320d3
--- /dev/null
+++ b/testcases/011-cpu-usage/i3status.conf
@@ -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
+}
diff --git a/testcases/011-cpu-usage/stat b/testcases/011-cpu-usage/stat
new file mode 100644
index 0000000..9c77e7e
--- /dev/null
+++ b/testcases/011-cpu-usage/stat
@@ -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
diff --git a/testcases/012-cpu-usage-error/expected_output.txt b/testcases/012-cpu-usage-error/expected_output.txt
new file mode 100644
index 0000000..3a74003
--- /dev/null
+++ b/testcases/012-cpu-usage-error/expected_output.txt
@@ -0,0 +1 @@
+cant read cpu usage
diff --git a/testcases/012-cpu-usage-error/i3status.conf b/testcases/012-cpu-usage-error/i3status.conf
new file mode 100644
index 0000000..285d415
--- /dev/null
+++ b/testcases/012-cpu-usage-error/i3status.conf
@@ -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
+}
diff --git a/testcases/012-cpu-usage-error/stat b/testcases/012-cpu-usage-error/stat
new file mode 100644
index 0000000..cb9b6e3
--- /dev/null
+++ b/testcases/012-cpu-usage-error/stat
@@ -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
diff --git a/testcases/013-cpu-usage-error/expected_output.txt b/testcases/013-cpu-usage-error/expected_output.txt
new file mode 100644
index 0000000..3a74003
--- /dev/null
+++ b/testcases/013-cpu-usage-error/expected_output.txt
@@ -0,0 +1 @@
+cant read cpu usage
diff --git a/testcases/013-cpu-usage-error/i3status.conf b/testcases/013-cpu-usage-error/i3status.conf
new file mode 100644
index 0000000..a55934e
--- /dev/null
+++ b/testcases/013-cpu-usage-error/i3status.conf
@@ -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
+}
diff --git a/testcases/013-cpu-usage-error/stat b/testcases/013-cpu-usage-error/stat
new file mode 100644
index 0000000..3ded833
--- /dev/null
+++ b/testcases/013-cpu-usage-error/stat
@@ -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