]> git.sur5r.net Git - i3/i3status/blob - src/print_cpu_usage.c
able to print percentage
[i3/i3status] / src / print_cpu_usage.c
1 // vim:ts=4:sw=4:expandtab
2 #include <stdlib.h>
3 #include <limits.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <yajl/yajl_gen.h>
7 #include <yajl/yajl_version.h>
8
9 #if defined(__FreeBSD__) || defined(__OpenBSD__)
10 #include <sys/param.h>
11 #include <sys/types.h>
12 #include <sys/sysctl.h>
13 #if defined(__OpenBSD__)
14 #include <sys/sched.h>
15 #else
16 #include <sys/dkstat.h>
17 #endif
18 #endif
19
20 #if defined(__DragonFly__)
21 #include <sys/param.h>
22 #include <sys/types.h>
23 #include <sys/sysctl.h>
24 #include <sys/resource.h>
25 #endif
26
27 #if defined(__NetBSD__)
28 #include <sys/param.h>
29 #include <sys/resource.h>
30 #include <sys/sysctl.h>
31 #include <sys/sched.h>
32 #endif
33
34 #include "i3status.h"
35
36 struct cpu_usage {
37     int user;
38     int nice;
39     int system;
40     int idle;
41     int total;
42 };
43
44 static int cpu_count = 0;
45 static struct cpu_usage prev_all = {0, 0, 0, 0, 0};
46 static struct cpu_usage *prev_cpus = NULL;
47 static struct cpu_usage *curr_cpus = NULL;
48
49 /*
50  * Reads the CPU utilization from /proc/stat and returns the usage as a
51  * percentage.
52  *
53  */
54 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) {
55     const char *selected_format = format;
56     const char *walk;
57     char *outwalk = buffer;
58     struct cpu_usage curr_all = {0, 0, 0, 0, 0};
59     int diff_idle, diff_total, diff_usage;
60     bool colorful_output = false;
61
62 #if defined(LINUX)
63
64     // Detecting if CPU count has changed
65     int curr_cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
66     if (curr_cpu_count != cpu_count) {
67         cpu_count = curr_cpu_count;
68         free(prev_cpus);
69         prev_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage));
70         free(curr_cpus);
71         curr_cpus = (struct cpu_usage *)calloc(cpu_count, sizeof(struct cpu_usage));
72     }
73
74     char buf[4096];
75     if (!slurp(path, buf, sizeof(buf)))
76         goto error;
77     // Parsing all cpu values using strtok
78     if (strtok(buf, "\n") == NULL)
79         goto error;
80     char *buf_itr = NULL;
81     for (int cpu_idx = 0; cpu_idx < cpu_count; cpu_idx++) {
82         buf_itr = strtok(NULL, "\n");
83         int curr_cpu_idx = -1;
84         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)
85             goto error;
86         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;
87         curr_all.user += curr_cpus[cpu_idx].user;
88         curr_all.nice += curr_cpus[cpu_idx].nice;
89         curr_all.system += curr_cpus[cpu_idx].system;
90         curr_all.idle += curr_cpus[cpu_idx].idle;
91         curr_all.total += curr_cpus[cpu_idx].total;
92     }
93
94     diff_idle = curr_all.idle - prev_all.idle;
95     diff_total = curr_all.total - prev_all.total;
96     diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0);
97     prev_all = curr_all;
98 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
99
100 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
101     size_t size;
102     long cp_time[CPUSTATES];
103     size = sizeof cp_time;
104     if (sysctlbyname("kern.cp_time", &cp_time, &size, NULL, 0) < 0)
105         goto error;
106 #else
107     /* This information is taken from the boot cpu, any other cpus are currently ignored. */
108     long cp_time[CPUSTATES];
109     int mib[2];
110     size_t size = sizeof(cp_time);
111
112     mib[0] = CTL_KERN;
113     mib[1] = KERN_CPTIME;
114
115     if (sysctl(mib, 2, cp_time, &size, NULL, 0))
116         goto error;
117 #endif
118
119     curr_all.user = cp_time[CP_USER];
120     curr_all.nice = cp_time[CP_NICE];
121     curr_all.system = cp_time[CP_SYS];
122     curr_all.idle = cp_time[CP_IDLE];
123     curr_all.total = curr_all.user + curr_all.nice + curr_all.system + curr_all.idle;
124     diff_idle = curr_all.idle - prev_all.idle;
125     diff_total = curr_all.total - prev_all.total;
126     diff_usage = (diff_total ? (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 : 0);
127     prev_all = curr_all;
128 #else
129     goto error;
130 #endif
131
132     if (diff_usage >= max_threshold) {
133         START_COLOR("color_bad");
134         colorful_output = true;
135         if (format_above_threshold != NULL)
136             selected_format = format_above_threshold;
137     } else if (diff_usage >= degraded_threshold) {
138         START_COLOR("color_degraded");
139         colorful_output = true;
140         if (format_above_degraded_threshold != NULL)
141             selected_format = format_above_degraded_threshold;
142     }
143
144     for (walk = selected_format; *walk != '\0'; walk++) {
145         if (*walk != '%') {
146             *(outwalk++) = *walk;
147
148         } else if (BEGINS_WITH(walk + 1, "usage")) {
149             outwalk += sprintf(outwalk, "%02d%s", diff_usage, pct_mark);
150             walk += strlen("usage");
151         }
152 #if defined(LINUX)
153         else if (BEGINS_WITH(walk + 1, "cpu")) {
154             int number = 0;
155             sscanf(walk + 1, "cpu%d", &number);
156             if (number < 0 || number >= cpu_count) {
157                 fprintf(stderr, "provided CPU number '%d' above detected number of CPU %d\n", number, cpu_count);
158             } else {
159                 int cpu_diff_idle = curr_cpus[number].idle - prev_cpus[number].idle;
160                 int cpu_diff_total = curr_cpus[number].total - prev_cpus[number].total;
161                 int cpu_diff_usage = (cpu_diff_total ? (1000 * (cpu_diff_total - cpu_diff_idle) / cpu_diff_total + 5) / 10 : 0);
162                 outwalk += sprintf(outwalk, "%02d%s", cpu_diff_usage, pct_mark);
163             }
164             int padding = 1;
165             int step = 10;
166             while (step < number) {
167                 step *= 10;
168                 padding++;
169             }
170             walk += strlen("cpu") + padding;
171         }
172 #endif
173         else {
174             *(outwalk++) = '%';
175         }
176     }
177
178     for (int i = 0; i < cpu_count; i++)
179         prev_cpus[i] = curr_cpus[i];
180
181     if (colorful_output)
182         END_COLOR;
183
184     OUTPUT_FULL_TEXT(buffer);
185     return;
186 error:
187     OUTPUT_FULL_TEXT("cant read cpu usage");
188     (void)fputs("i3status: Cannot read CPU usage\n", stderr);
189 }