]> git.sur5r.net Git - i3/i3status/commitdiff
Multiple CPU support for cpu_usage (#209)
authoreplanet <emeric.planet@gmail.com>
Sun, 26 Mar 2017 10:54:07 +0000 (12:54 +0200)
committerMichael Stapelberg <stapelberg@users.noreply.github.com>
Sun, 26 Mar 2017 10:54:07 +0000 (06:54 -0400)
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.

16 files changed:
i3status.c
include/i3status.h
man/i3status.man
src/print_cpu_usage.c
testcases/010-cpu-usage/expected_output.txt [new file with mode: 0644]
testcases/010-cpu-usage/i3status.conf [new file with mode: 0644]
testcases/010-cpu-usage/stat [new file with mode: 0644]
testcases/011-cpu-usage/expected_output.txt [new file with mode: 0644]
testcases/011-cpu-usage/i3status.conf [new file with mode: 0644]
testcases/011-cpu-usage/stat [new file with mode: 0644]
testcases/012-cpu-usage-error/expected_output.txt [new file with mode: 0644]
testcases/012-cpu-usage-error/i3status.conf [new file with mode: 0644]
testcases/012-cpu-usage-error/stat [new file with mode: 0644]
testcases/013-cpu-usage-error/expected_output.txt [new file with mode: 0644]
testcases/013-cpu-usage-error/i3status.conf [new file with mode: 0644]
testcases/013-cpu-usage-error/stat [new file with mode: 0644]

index 3b9ab7214681d458356b4ef9b5c3db8becf0a753..558f8ab311623eff122afbe071745e45e84c950b 100644 (file)
@@ -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;
             }
         }
index a0e1e31b7b6a5276438257e5d26f35dffdb9df15..5042832d08bbbc0b809e25488719735a174ae1d0 100644 (file)
@@ -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);
index 22d51caa6a5e870b28012df621a6d75bcb0c3499..f1b622193814e7219e1578e2727bf94b618c0c28 100644 (file)
@@ -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+
 
index 45a5ef250f7ee1050f6f98b96b200a7a439ddb8e..c1ea3fd4b1576cfa0c5a0d69014f7d25dfc0d971 100644 (file)
 
 #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 (file)
index 0000000..336596e
--- /dev/null
@@ -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 (file)
index 0000000..57cddf2
--- /dev/null
@@ -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 (file)
index 0000000..6fbc94e
--- /dev/null
@@ -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 (file)
index 0000000..930a2b5
--- /dev/null
@@ -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 (file)
index 0000000..39320d3
--- /dev/null
@@ -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 (file)
index 0000000..9c77e7e
--- /dev/null
@@ -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 (file)
index 0000000..3a74003
--- /dev/null
@@ -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 (file)
index 0000000..285d415
--- /dev/null
@@ -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 (file)
index 0000000..cb9b6e3
--- /dev/null
@@ -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 (file)
index 0000000..3a74003
--- /dev/null
@@ -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 (file)
index 0000000..a55934e
--- /dev/null
@@ -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 (file)
index 0000000..3ded833
--- /dev/null
@@ -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