1 /* From wmload.c, v0.9.2, licensed under the GPL. */
2 #include <config.h>
3 #include <sys/types.h>
4 #include <sys/statvfs.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7
8 #include <glibtop.h>
9 #include <glibtop/cpu.h>
10 #include <glibtop/mem.h>
11 #include <glibtop/swap.h>
12 #include <glibtop/loadavg.h>
13 #include <glibtop/netload.h>
14 #include <glibtop/netlist.h>
15 #include <glibtop/mountlist.h>
16 #include <glibtop/fsusage.h>
17
18 #include "linux-proc.h"
19 #include "autoscaler.h"
20
21 static const unsigned needed_cpu_flags =
22 (1 << GLIBTOP_CPU_USER) +
23 (1 << GLIBTOP_CPU_IDLE) +
24 (1 << GLIBTOP_CPU_SYS) +
25 (1 << GLIBTOP_CPU_NICE);
26
27 #if 0
28 static const unsigned needed_page_flags =
29 (1 << GLIBTOP_SWAP_PAGEIN) +
30 (1 << GLIBTOP_SWAP_PAGEOUT);
31 #endif
32
33 static const unsigned needed_mem_flags =
34 (1 << GLIBTOP_MEM_USED) +
35 (1 << GLIBTOP_MEM_FREE);
36
37 static const unsigned needed_swap_flags =
38 (1 << GLIBTOP_SWAP_USED) +
39 (1 << GLIBTOP_SWAP_FREE);
40
41 static const unsigned needed_loadavg_flags =
42 (1 << GLIBTOP_LOADAVG_LOADAVG);
43
44 static const unsigned needed_netload_flags =
45 (1 << GLIBTOP_NETLOAD_IF_FLAGS) +
46 (1 << GLIBTOP_NETLOAD_BYTES_TOTAL);
47
48 void
GetLoad(guint64 Maximum,guint64 data[cpuload_n],LoadGraph * g)49 GetLoad (guint64 Maximum,
50 guint64 data [cpuload_n],
51 LoadGraph *g)
52 {
53 MultiloadApplet *multiload;
54 glibtop_cpu cpu;
55 guint64 cpu_aux [cpuload_n], used = 0, total = 0;
56 guint64 current_scaled, used_scaled = 0;
57 unsigned i;
58
59 glibtop_get_cpu (&cpu);
60
61 g_return_if_fail ((cpu.flags & needed_cpu_flags) == needed_cpu_flags);
62
63 multiload = g->multiload;
64
65 multiload->cpu_time [cpuload_usr] = cpu.user;
66 multiload->cpu_time [cpuload_nice] = cpu.nice;
67 multiload->cpu_time [cpuload_sys] = cpu.sys;
68 multiload->cpu_time [cpuload_iowait] = cpu.iowait + cpu.irq + cpu.softirq;
69 multiload->cpu_time [cpuload_free] = cpu.idle;
70
71 if (!multiload->cpu_initialized) {
72 memcpy (multiload->cpu_last, multiload->cpu_time, sizeof (multiload->cpu_last));
73 multiload->cpu_initialized = TRUE;
74 }
75
76 for (i = 0; i < cpuload_n; i++) {
77 cpu_aux [i] = multiload->cpu_time [i] - multiload->cpu_last [i];
78 total += cpu_aux [i];
79 }
80
81 for (i = 0; i < cpuload_free; i++) {
82 used += cpu_aux [i];
83 current_scaled = (guint64) ((float)(cpu_aux [i] * Maximum) / (float)total);
84 used_scaled += current_scaled;
85 data [i] = current_scaled;
86 }
87 data [cpuload_free] = Maximum - used_scaled;
88
89 multiload->cpu_used_ratio = (float)(used) / (float)total;
90
91 memcpy (multiload->cpu_last, multiload->cpu_time, sizeof multiload->cpu_last);
92 }
93
94 void
GetDiskLoad(guint64 Maximum,guint64 data[diskload_n],LoadGraph * g)95 GetDiskLoad (guint64 Maximum,
96 guint64 data [diskload_n],
97 LoadGraph *g)
98 {
99 static gboolean first_call = TRUE;
100 static guint64 lastread = 0, lastwrite = 0;
101 static AutoScaler scaler;
102
103 guint64 max;
104 guint64 read, write;
105 guint64 readdiff, writediff;
106 guint i;
107
108 MultiloadApplet *multiload;
109
110 multiload = g->multiload;
111
112
113 if(first_call)
114 {
115 autoscaler_init (&scaler, g->speed, 500);
116 }
117
118 read = write = 0;
119
120 if (multiload->nvme_diskstats)
121 {
122 FILE *fdr;
123 char line[255];
124 guint64 s_read, s_write;
125
126 fdr = fopen("/proc/diskstats", "r");
127 if (!fdr)
128 {
129 multiload->nvme_diskstats = FALSE;
130 g_settings_set_boolean (multiload->settings, "diskload-nvme-diskstats", FALSE);
131 return;
132 }
133
134 while (fgets(line, 255, fdr))
135 {
136 /* Match main device, rather than individual partitions (e.g. nvme0n1) */
137 if (!g_regex_match_simple("\\snvme\\d+\\w+\\d+\\s", line, 0, 0))
138 {
139 continue;
140 }
141
142 /*
143 6 - sectors read
144 10 - sectors written
145 */
146 if (sscanf(line, "%*d %*d %*s %*d %*d %ld %*d %*d %*d %ld", &s_read, &s_write) == 2)
147 {
148 read += 512 * s_read;
149 write += 512 * s_write;
150 }
151 }
152 fclose(fdr);
153 }
154 else
155 {
156 glibtop_mountlist mountlist;
157 glibtop_mountentry *mountentries;
158
159 mountentries = glibtop_get_mountlist (&mountlist, FALSE);
160
161 for (i = 0; i < mountlist.number; i++)
162 {
163 struct statvfs statresult;
164 glibtop_fsusage fsusage;
165
166 if (strstr (mountentries[i].devname, "/dev/") == NULL)
167 continue;
168
169 if (strstr (mountentries[i].mountdir, "/media/") != NULL)
170 continue;
171
172 if (statvfs (mountentries[i].mountdir, &statresult) < 0)
173 {
174 g_debug ("Failed to get statistics for mount entry: %s. Reason: %s. Skipping entry.",
175 mountentries[i].mountdir, strerror(errno));
176 continue;
177 }
178
179 glibtop_get_fsusage(&fsusage, mountentries[i].mountdir);
180 read += fsusage.read;
181 write += fsusage.write;
182 }
183
184 g_free(mountentries);
185 }
186
187 readdiff = read - lastread;
188 writediff = write - lastwrite;
189
190 lastread = read;
191 lastwrite = write;
192
193 if(first_call)
194 {
195 first_call = FALSE;
196 memset(data, 0, 3 * sizeof data[0]);
197 return;
198 }
199
200 max = autoscaler_get_max(&scaler, readdiff + writediff);
201
202 multiload->diskload_used_ratio = (float)(readdiff + writediff) / (float)max;
203
204 data [diskload_read] = (guint64) ((float)Maximum * (float)readdiff / (float)max);
205 data [diskload_write] = (guint64) ((float)Maximum * (float)writediff / (float)max);
206 data [diskload_free] = Maximum - (data [0] + data[1]);
207 }
208
209 /* GNU/Linux:
210 * aux [memload_user] = (mem.total - mem.free) - (mem.cached + mem.buffer)
211 * aux [memload_shared] = mem.shared;
212 * aux [memload_cached] = mem.cached - mem.shared;
213 * aux [memload_buffer] = mem.buffer;
214 *
215 * Other operating systems:
216 * aux [memload_user] = mem.user;
217 * aux [memload_shared] = mem.shared;
218 * aux [memload_cached] = mem.cached;
219 * aux [memload_buffer] = mem.buffer;
220 */
221 void
GetMemory(guint64 Maximum,guint64 data[memload_n],LoadGraph * g)222 GetMemory (guint64 Maximum,
223 guint64 data [memload_n],
224 LoadGraph *g)
225 {
226 MultiloadApplet *multiload;
227 glibtop_mem mem;
228 guint64 aux [memload_n], cache = 0;
229 guint64 current_scaled, used_scaled = 0;
230 int i;
231
232 glibtop_get_mem (&mem);
233
234 g_return_if_fail ((mem.flags & needed_mem_flags) == needed_mem_flags);
235
236 #ifndef __linux__
237 aux [memload_user] = mem.user;
238 aux [memload_cached] = mem.cached;
239 #else
240 aux [memload_user] = mem.total - mem.free - mem.buffer - mem.cached;;
241 aux [memload_cached] = mem.cached - mem.shared;
242 #endif /* __linux__ */
243 aux [memload_shared] = mem.shared;
244 aux [memload_buffer] = mem.buffer;
245
246 for (i = 0; i < memload_free; i++) {
247 current_scaled = (guint64) ((float)(aux [i] * Maximum) / (float)mem.total);
248 if (i != memload_user) {
249 cache += aux [i];
250 }
251 used_scaled += current_scaled;
252 data [i] = current_scaled;
253 }
254 data [memload_free] = MAX (Maximum - used_scaled, 0);
255
256 multiload = g->multiload;
257 multiload->memload_user = aux [memload_user];
258 multiload->memload_cache = cache;
259 multiload->memload_total = mem.total;
260 }
261
262 void
GetSwap(guint64 Maximum,guint64 data[swapload_n],LoadGraph * g)263 GetSwap (guint64 Maximum,
264 guint64 data [swapload_n],
265 LoadGraph *g)
266 {
267 guint64 used;
268 MultiloadApplet *multiload;
269 glibtop_swap swap;
270
271 glibtop_get_swap (&swap);
272 g_return_if_fail ((swap.flags & needed_swap_flags) == needed_swap_flags);
273
274 multiload = g->multiload;
275
276 if (swap.total == 0) {
277 used = 0;
278 multiload->swapload_used_ratio = 0.0f;
279 }
280 else {
281 float ratio;
282
283 ratio = (float)swap.used / (float)swap.total;
284 used = (guint64) ((float) Maximum * ratio);
285 multiload->swapload_used_ratio = ratio;
286 }
287
288 data [0] = used;
289 data [1] = Maximum - used;
290 }
291
292 void
GetLoadAvg(guint64 Maximum,guint64 data[2],LoadGraph * g)293 GetLoadAvg (guint64 Maximum,
294 guint64 data [2],
295 LoadGraph *g)
296 {
297 glibtop_loadavg loadavg;
298 MultiloadApplet *multiload;
299
300 glibtop_get_loadavg (&loadavg);
301
302 g_return_if_fail ((loadavg.flags & needed_loadavg_flags) == needed_loadavg_flags);
303
304 multiload = g->multiload;
305 multiload->loadavg1 = loadavg.loadavg[0];
306
307 data [0] = (guint64) ((float) Maximum * loadavg.loadavg[0]);
308 data [1] = Maximum - data[0];
309 }
310
311 /*
312 * Return true if a network device (identified by its name) is virtual
313 * (ie: not corresponding to a physical device). In case it is a physical
314 * device or unknown, returns false.
315 */
316 static gboolean
is_net_device_virtual(char * device)317 is_net_device_virtual(char *device)
318 {
319 /*
320 * There is not definitive way to find out. On some systems (Linux
321 * kernels ≳ 2.19 without option SYSFS_DEPRECATED), there exist a
322 * directory /sys/devices/virtual/net which only contains virtual
323 * devices. It's also possible to detect by the fact that virtual
324 * devices do not have a symlink "device" in
325 * /sys/class/net/name-of-dev/ . This second method is more complex
326 * but more reliable.
327 */
328 gboolean ret = FALSE;
329 char *path = malloc (strlen (device) + strlen ("/sys/class/net//device") + 1);
330
331 if (path == NULL)
332 return FALSE;
333
334 /* Check if /sys/class/net/name-of-dev/ exists (may be old linux kernel
335 * or not linux at all). */
336 do {
337 if (sprintf(path, "/sys/class/net/%s", device) < 0)
338 break;
339 if (access(path, F_OK) != 0)
340 break; /* unknown */
341
342 if (sprintf(path, "/sys/class/net/%s/device", device) < 0)
343 break;
344 if (access(path, F_OK) != 0)
345 ret = TRUE;
346 } while (0);
347
348 free (path);
349 return ret;
350 }
351
352 void
GetNet(guint64 Maximum,guint64 data[4],LoadGraph * g)353 GetNet (guint64 Maximum,
354 guint64 data [4],
355 LoadGraph *g)
356 {
357 enum Types {
358 IN_COUNT = 0,
359 OUT_COUNT = 1,
360 LOCAL_COUNT = 2,
361 COUNT_TYPES = 3
362 };
363
364 static int ticks = 0;
365 static guint64 past[COUNT_TYPES] = {0};
366
367 guint64 present[COUNT_TYPES] = {0};
368
369 guint i;
370 gchar **devices;
371 glibtop_netlist netlist;
372
373 MultiloadApplet *multiload;
374
375 multiload = g->multiload;
376 devices = glibtop_get_netlist(&netlist);
377
378 for(i = 0; i < netlist.number; ++i)
379 {
380 glibtop_netload netload;
381
382 glibtop_get_netload(&netload, devices[i]);
383
384 g_return_if_fail((netload.flags & needed_netload_flags) == needed_netload_flags);
385
386 if (!(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP)))
387 continue;
388
389 if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) {
390 /* for loopback in and out are identical, so only count in */
391 present[LOCAL_COUNT] += netload.bytes_in;
392 continue;
393 }
394
395 /*
396 * Do not include virtual devices (VPN, PPPOE...) to avoid
397 * counting the same throughput several times.
398 */
399 if (is_net_device_virtual(devices[i]))
400 continue;
401
402 present[IN_COUNT] += netload.bytes_in;
403 present[OUT_COUNT] += netload.bytes_out;
404 }
405
406 g_strfreev(devices);
407 netspeed_add (multiload->netspeed_in, present[IN_COUNT]);
408 netspeed_add (multiload->netspeed_out, present[OUT_COUNT]);
409
410 if(ticks < 2) /* avoid initial spike */
411 {
412 ticks++;
413 memset(data, 0, (COUNT_TYPES + 1) * sizeof data[0]);
414 }
415 else
416 {
417 data[COUNT_TYPES] = 0;
418 float seconds = (float) g->speed / 1000.0f;
419 for (i = 0; i < COUNT_TYPES; i++)
420 {
421 /* protect against weirdness */
422 if (present[i] >= past[i])
423 data[i] = (guint) ((float) (present[i] - past[i]) / seconds);
424 else
425 data[i] = 0;
426 data[COUNT_TYPES] += data[i];
427 }
428 }
429
430 memcpy(past, present, sizeof past);
431 }
432