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