@cryptotaxi247 / netdata-1 / commits / 7af362ca5

added support for monitoring cgroups

Costa Tsaousis (ktsaou) committed Apr 24, 2016 at 04:07 UTC 7af362ca5bce724d23bbe5d84f7c13efd6944eb6
9 files changed +1342 -38
CMakeLists.txt
+1
@@ -64,6 +64,7 @@ set(NETDATA_SOURCE_FILES
64 src/storage_number.c
65 src/storage_number.h
66 src/sys_kernel_mm_ksm.c
67 + src/sys_fs_cgroup.c
68 src/unit_test.c
69 src/unit_test.h
70 src/url.c
src/Makefile.am
+1
@@ -55,6 +55,7 @@ netdata_SOURCES = \
55 proc_sys_kernel_random_entropy_avail.c \
56 proc_vmstat.c \
57 sys_kernel_mm_ksm.c \
58 + sys_fs_cgroup.c \
59 procfile.c procfile.h \
60 rrd.c rrd.h \
61 rrd2json.c rrd2json.h \
src/apps_plugin.c
+6 -30
@@ -43,8 +43,6 @@
43 #define MAX_NAME 100
44 #define MAX_CMDLINE 1024
45
46 -unsigned long long Hertz = 1;
47 -
46 long processors = 1;
47 long pid_max = 32768;
48 int debug = 0;
@@ -267,28 +265,6 @@ long get_system_pid_max(void) {
265 return mpid;
266 }
267
270 -unsigned long long get_system_hertz(void)
271 -{
272 - unsigned long long myhz = 1;
273 -
274 -#ifdef _SC_CLK_TCK
275 - if((myhz = (unsigned long long int) sysconf(_SC_CLK_TCK)) > 0) {
276 - return myhz;
277 - }
278 -#endif
279 -
280 -#ifdef HZ
281 - myhz = HZ; /* <asm/param.h> */
282 -#else /* HZ */
283 - /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
284 - hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
285 -#endif /* HZ */
286 -
287 - error("Unknown HZ value. Assuming %llu.", myhz);
288 - return myhz;
289 -}
290 -
291 -
268 // ----------------------------------------------------------------------------
269 // target
270 // target is the structure that process data are aggregated
@@ -410,7 +386,7 @@ struct target *get_users_target(uid_t uid)
386 else
387 snprintf(w->name, MAX_NAME, "%s", pw->pw_name);
388
413 - netdata_fix_id(w->name);
389 + netdata_fix_chart_name(w->name);
390
391 w->uid = uid;
392
@@ -448,7 +424,7 @@ struct target *get_groups_target(gid_t gid)
424 else
425 snprintf(w->name, MAX_NAME, "%s", gr->gr_name);
426
451 - netdata_fix_id(w->name);
427 + netdata_fix_chart_name(w->name);
428
429 w->gid = gid;
430
@@ -2267,7 +2243,7 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
2243 for (w = root; w ; w = w->next) {
2244 if(w->target || (!w->processes && !w->exposed)) continue;
2245
2270 - fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, Hertz, w->hidden ? "hidden,noreset" : "noreset");
2246 + fprintf(stdout, "DIMENSION %s '' incremental 100 %u %s\n", w->name, hz, w->hidden ? "hidden,noreset" : "noreset");
2247 }
2248
2249 fprintf(stdout, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
@@ -2295,14 +2271,14 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
2271 for (w = root; w ; w = w->next) {
2272 if(w->target || (!w->processes && !w->exposed)) continue;
2273
2298 - fprintf(stdout, "DIMENSION %s '' incremental 100 %llu noreset\n", w->name, Hertz * processors);
2274 + fprintf(stdout, "DIMENSION %s '' incremental 100 %ld noreset\n", w->name, hz * processors);
2275 }
2276
2277 fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
2278 for (w = root; w ; w = w->next) {
2279 if(w->target || (!w->processes && !w->exposed)) continue;
2280
2305 - fprintf(stdout, "DIMENSION %s '' incremental 100 %llu noreset\n", w->name, Hertz * processors);
2281 + fprintf(stdout, "DIMENSION %s '' incremental 100 %ld noreset\n", w->name, hz * processors);
2282 }
2283
2284 fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
@@ -2447,7 +2423,7 @@ int main(int argc, char **argv)
2423
2424 time_t started_t = time(NULL);
2425 time_t current_t;
2450 - Hertz = get_system_hertz();
2426 + get_HZ();
2427 pid_max = get_system_pid_max();
2428 processors = get_system_cpus();
2429
src/common.c
+268 -3
@@ -20,7 +20,7 @@
20 char *global_host_prefix = "";
21 int enable_ksm = 1;
22
23 -unsigned char netdata_keys_map[256] = {
23 +unsigned char netdata_map_chart_names[256] = {
24 [0] = '\0', //
25 [1] = '_', //
26 [2] = '_', //
@@ -281,8 +281,273 @@ unsigned char netdata_keys_map[256] = {
281
282 // make sure the supplied string
283 // is good for a netdata chart/dimension ID/NAME
284 -void netdata_fix_id(char *s) {
285 - while((*s = netdata_keys_map[(unsigned char)*s])) s++;
284 +void netdata_fix_chart_name(char *s) {
285 + while((*s = netdata_map_chart_names[(unsigned char)*s])) s++;
286 +}
287 +
288 +unsigned char netdata_map_chart_ids[256] = {
289 + [0] = '\0', //
290 + [1] = '_', //
291 + [2] = '_', //
292 + [3] = '_', //
293 + [4] = '_', //
294 + [5] = '_', //
295 + [6] = '_', //
296 + [7] = '_', //
297 + [8] = '_', //
298 + [9] = '_', //
299 + [10] = '_', //
300 + [11] = '_', //
301 + [12] = '_', //
302 + [13] = '_', //
303 + [14] = '_', //
304 + [15] = '_', //
305 + [16] = '_', //
306 + [17] = '_', //
307 + [18] = '_', //
308 + [19] = '_', //
309 + [20] = '_', //
310 + [21] = '_', //
311 + [22] = '_', //
312 + [23] = '_', //
313 + [24] = '_', //
314 + [25] = '_', //
315 + [26] = '_', //
316 + [27] = '_', //
317 + [28] = '_', //
318 + [29] = '_', //
319 + [30] = '_', //
320 + [31] = '_', //
321 + [32] = '_', //
322 + [33] = '_', // !
323 + [34] = '_', // "
324 + [35] = '_', // #
325 + [36] = '_', // $
326 + [37] = '_', // %
327 + [38] = '_', // &
328 + [39] = '_', // '
329 + [40] = '_', // (
330 + [41] = '_', // )
331 + [42] = '_', // *
332 + [43] = '_', // +
333 + [44] = '.', // ,
334 + [45] = '-', // -
335 + [46] = '.', // .
336 + [47] = '_', // /
337 + [48] = '0', // 0
338 + [49] = '1', // 1
339 + [50] = '2', // 2
340 + [51] = '3', // 3
341 + [52] = '4', // 4
342 + [53] = '5', // 5
343 + [54] = '6', // 6
344 + [55] = '7', // 7
345 + [56] = '8', // 8
346 + [57] = '9', // 9
347 + [58] = '_', // :
348 + [59] = '_', // ;
349 + [60] = '_', // <
350 + [61] = '_', // =
351 + [62] = '_', // >
352 + [63] = '_', // ?
353 + [64] = '_', // @
354 + [65] = 'a', // A
355 + [66] = 'b', // B
356 + [67] = 'c', // C
357 + [68] = 'd', // D
358 + [69] = 'e', // E
359 + [70] = 'f', // F
360 + [71] = 'g', // G
361 + [72] = 'h', // H
362 + [73] = 'i', // I
363 + [74] = 'j', // J
364 + [75] = 'k', // K
365 + [76] = 'l', // L
366 + [77] = 'm', // M
367 + [78] = 'n', // N
368 + [79] = 'o', // O
369 + [80] = 'p', // P
370 + [81] = 'q', // Q
371 + [82] = 'r', // R
372 + [83] = 's', // S
373 + [84] = 't', // T
374 + [85] = 'u', // U
375 + [86] = 'v', // V
376 + [87] = 'w', // W
377 + [88] = 'x', // X
378 + [89] = 'y', // Y
379 + [90] = 'z', // Z
380 + [91] = '_', // [
381 + [92] = '/', // backslash
382 + [93] = '_', // ]
383 + [94] = '_', // ^
384 + [95] = '_', // _
385 + [96] = '_', // `
386 + [97] = 'a', // a
387 + [98] = 'b', // b
388 + [99] = 'c', // c
389 + [100] = 'd', // d
390 + [101] = 'e', // e
391 + [102] = 'f', // f
392 + [103] = 'g', // g
393 + [104] = 'h', // h
394 + [105] = 'i', // i
395 + [106] = 'j', // j
396 + [107] = 'k', // k
397 + [108] = 'l', // l
398 + [109] = 'm', // m
399 + [110] = 'n', // n
400 + [111] = 'o', // o
401 + [112] = 'p', // p
402 + [113] = 'q', // q
403 + [114] = 'r', // r
404 + [115] = 's', // s
405 + [116] = 't', // t
406 + [117] = 'u', // u
407 + [118] = 'v', // v
408 + [119] = 'w', // w
409 + [120] = 'x', // x
410 + [121] = 'y', // y
411 + [122] = 'z', // z
412 + [123] = '_', // {
413 + [124] = '_', // |
414 + [125] = '_', // }
415 + [126] = '_', // ~
416 + [127] = '_', //
417 + [128] = '_', //
418 + [129] = '_', //
419 + [130] = '_', //
420 + [131] = '_', //
421 + [132] = '_', //
422 + [133] = '_', //
423 + [134] = '_', //
424 + [135] = '_', //
425 + [136] = '_', //
426 + [137] = '_', //
427 + [138] = '_', //
428 + [139] = '_', //
429 + [140] = '_', //
430 + [141] = '_', //
431 + [142] = '_', //
432 + [143] = '_', //
433 + [144] = '_', //
434 + [145] = '_', //
435 + [146] = '_', //
436 + [147] = '_', //
437 + [148] = '_', //
438 + [149] = '_', //
439 + [150] = '_', //
440 + [151] = '_', //
441 + [152] = '_', //
442 + [153] = '_', //
443 + [154] = '_', //
444 + [155] = '_', //
445 + [156] = '_', //
446 + [157] = '_', //
447 + [158] = '_', //
448 + [159] = '_', //
449 + [160] = '_', //
450 + [161] = '_', //
451 + [162] = '_', //
452 + [163] = '_', //
453 + [164] = '_', //
454 + [165] = '_', //
455 + [166] = '_', //
456 + [167] = '_', //
457 + [168] = '_', //
458 + [169] = '_', //
459 + [170] = '_', //
460 + [171] = '_', //
461 + [172] = '_', //
462 + [173] = '_', //
463 + [174] = '_', //
464 + [175] = '_', //
465 + [176] = '_', //
466 + [177] = '_', //
467 + [178] = '_', //
468 + [179] = '_', //
469 + [180] = '_', //
470 + [181] = '_', //
471 + [182] = '_', //
472 + [183] = '_', //
473 + [184] = '_', //
474 + [185] = '_', //
475 + [186] = '_', //
476 + [187] = '_', //
477 + [188] = '_', //
478 + [189] = '_', //
479 + [190] = '_', //
480 + [191] = '_', //
481 + [192] = '_', //
482 + [193] = '_', //
483 + [194] = '_', //
484 + [195] = '_', //
485 + [196] = '_', //
486 + [197] = '_', //
487 + [198] = '_', //
488 + [199] = '_', //
489 + [200] = '_', //
490 + [201] = '_', //
491 + [202] = '_', //
492 + [203] = '_', //
493 + [204] = '_', //
494 + [205] = '_', //
495 + [206] = '_', //
496 + [207] = '_', //
497 + [208] = '_', //
498 + [209] = '_', //
499 + [210] = '_', //
500 + [211] = '_', //
501 + [212] = '_', //
502 + [213] = '_', //
503 + [214] = '_', //
504 + [215] = '_', //
505 + [216] = '_', //
506 + [217] = '_', //
507 + [218] = '_', //
508 + [219] = '_', //
509 + [220] = '_', //
510 + [221] = '_', //
511 + [222] = '_', //
512 + [223] = '_', //
513 + [224] = '_', //
514 + [225] = '_', //
515 + [226] = '_', //
516 + [227] = '_', //
517 + [228] = '_', //
518 + [229] = '_', //
519 + [230] = '_', //
520 + [231] = '_', //
521 + [232] = '_', //
522 + [233] = '_', //
523 + [234] = '_', //
524 + [235] = '_', //
525 + [236] = '_', //
526 + [237] = '_', //
527 + [238] = '_', //
528 + [239] = '_', //
529 + [240] = '_', //
530 + [241] = '_', //
531 + [242] = '_', //
532 + [243] = '_', //
533 + [244] = '_', //
534 + [245] = '_', //
535 + [246] = '_', //
536 + [247] = '_', //
537 + [248] = '_', //
538 + [249] = '_', //
539 + [250] = '_', //
540 + [251] = '_', //
541 + [252] = '_', //
542 + [253] = '_', //
543 + [254] = '_', //
544 + [255] = '_' //
545 +};
546 +
547 +// make sure the supplied string
548 +// is good for a netdata chart/dimension ID/NAME
549 +void netdata_fix_chart_id(char *s) {
550 + while((*s = netdata_map_chart_ids[(unsigned char)*s])) s++;
551 }
552
553 /*
src/common.h
+2 -2
@@ -15,7 +15,8 @@
15 #define abs(x) ((x < 0)? -x : x)
16 #define usecdiff(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
17
18 -extern void netdata_fix_id(char *s);
18 +extern void netdata_fix_chart_id(char *s);
19 +extern void netdata_fix_chart_name(char *s);
20
21 extern uint32_t simple_hash(const char *name);
22 extern void strreverse(char* begin, char* end);
@@ -31,7 +32,6 @@ extern char *global_host_prefix;
32 extern int enable_ksm;
33
34 /* Number of ticks per second */
34 -#define HZ myhz
35 extern unsigned int hz;
36 extern void get_HZ(void);
37
src/plugin_proc.c
+10
@@ -58,6 +58,7 @@ void *proc_main(void *ptr)
58 int vdo_proc_loadavg = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
59 int vdo_sys_kernel_mm_ksm = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
60 int vdo_cpu_netdata = !config_get_boolean("plugin:proc", "netdata server resources", 1);
61 + int vdo_sys_fs_cgroup = !config_get_boolean("plugin:proc", "cgroups", 1);
62
63 // keep track of the time each module was called
64 unsigned long long sutime_proc_net_dev = 0ULL;
@@ -77,6 +78,7 @@ void *proc_main(void *ptr)
78 unsigned long long sutime_proc_softirqs = 0ULL;
79 unsigned long long sutime_proc_loadavg = 0ULL;
80 unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
81 + unsigned long long sutime_sys_fs_cgroup = 0ULL;
82
83 // the next time we will run - aligned properly
84 unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
@@ -236,6 +238,14 @@ void *proc_main(void *ptr)
238 }
239 if(unlikely(netdata_exit)) break;
240
241 + if(!vdo_sys_fs_cgroup) {
242 + debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
243 + sunow = sutime();
244 + vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
245 + sutime_sys_fs_cgroup = sunow;
246 + }
247 + if(unlikely(netdata_exit)) break;
248 +
249 // END -- the job is done
250
251 // --------------------------------------------------------------------
src/plugin_proc.h
+1
@@ -20,5 +20,6 @@ extern int do_proc_softirqs(int update_every, unsigned long long dt);
20 extern int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt);
21 extern int do_proc_loadavg(int update_every, unsigned long long dt);
22 extern int do_proc_net_stat_synproxy(int update_every, unsigned long long dt);
23 +extern int do_sys_fs_cgroup(int update_every, unsigned long long dt);
24
25 #endif /* NETDATA_PLUGIN_PROC_H */
src/sys_fs_cgroup.c new
+1021
@@ -0,0 +1,1021 @@
1 +#ifdef HAVE_CONFIG_H
2 +#include <config.h>
3 +#endif
4 +
5 +#include <stdio.h>
6 +#include <stdlib.h>
7 +#include <inttypes.h>
8 +#include <sys/types.h>
9 +#include <dirent.h>
10 +#include <string.h>
11 +
12 +#include "common.h"
13 +#include "appconfig.h"
14 +#include "procfile.h"
15 +#include "log.h"
16 +#include "rrd.h"
17 +
18 +// ----------------------------------------------------------------------------
19 +// cgroup globals
20 +
21 +static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
22 +static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
23 +static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
24 +static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
25 +static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
26 +static int cgroup_check_for_new_every = 10;
27 +static char *cgroup_cpuacct_base = NULL;
28 +static char *cgroup_blkio_base = NULL;
29 +static char *cgroup_memory_base = NULL;
30 +
31 +static int cgroup_root_count = 0;
32 +static int cgroup_root_max = 50;
33 +static int cgroup_max_depth = 0;
34 +
35 +void read_cgroup_plugin_configuration() {
36 + cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new plugin every", cgroup_check_for_new_every);
37 +
38 + cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
39 + cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
40 + cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
41 + cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
42 +
43 + char filename[FILENAME_MAX + 1];
44 + snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/cpuacct");
45 + cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
46 +
47 + snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/blkio");
48 + cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
49 +
50 + snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/memory");
51 + cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
52 +
53 + cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
54 + cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
55 +
56 + cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable cgroups detected after first run", cgroup_enable_new_cgroups_detected_at_runtime);
57 +}
58 +
59 +// ----------------------------------------------------------------------------
60 +// cgroup objects
61 +
62 +struct blkio {
63 + int updated;
64 +
65 + char *filename;
66 +
67 + unsigned long long Read;
68 + unsigned long long Write;
69 + unsigned long long Sync;
70 + unsigned long long Async;
71 + unsigned long long Total;
72 +};
73 +
74 +// https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
75 +struct memory {
76 + int updated;
77 +
78 + char *filename;
79 +
80 + unsigned long long cache;
81 + unsigned long long rss;
82 + unsigned long long rss_huge;
83 + unsigned long long mapped_file;
84 + unsigned long long writeback;
85 + unsigned long long pgpgin;
86 + unsigned long long pgpgout;
87 + unsigned long long pgfault;
88 + unsigned long long pgmajfault;
89 +/* unsigned long long inactive_anon;
90 + unsigned long long active_anon;
91 + unsigned long long inactive_file;
92 + unsigned long long active_file;
93 + unsigned long long unevictable;
94 + unsigned long long hierarchical_memory_limit;
95 + unsigned long long total_cache;
96 + unsigned long long total_rss;
97 + unsigned long long total_rss_huge;
98 + unsigned long long total_mapped_file;
99 + unsigned long long total_writeback;
100 + unsigned long long total_pgpgin;
101 + unsigned long long total_pgpgout;
102 + unsigned long long total_pgfault;
103 + unsigned long long total_pgmajfault;
104 + unsigned long long total_inactive_anon;
105 + unsigned long long total_active_anon;
106 + unsigned long long total_inactive_file;
107 + unsigned long long total_active_file;
108 + unsigned long long total_unevictable;
109 +*/
110 +};
111 +
112 +// https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
113 +struct cpuacct_stat {
114 + int updated;
115 +
116 + char *filename;
117 +
118 + unsigned long long user;
119 + unsigned long long system;
120 +};
121 +
122 +// https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
123 +struct cpuacct_usage {
124 + int updated;
125 +
126 + char *filename;
127 +
128 + unsigned int cpus;
129 + unsigned long long *cpu_percpu;
130 +};
131 +
132 +struct cgroup {
133 + int available;
134 + int enabled;
135 +
136 + char *id;
137 + uint32_t hash;
138 +
139 + char *name;
140 +
141 + struct cpuacct_stat cpuacct_stat;
142 + struct cpuacct_usage cpuacct_usage;
143 +
144 + struct memory memory;
145 +
146 + struct blkio io_service_bytes; // bytes
147 + struct blkio io_serviced; // operations
148 +
149 + struct blkio throttle_io_service_bytes; // bytes
150 + struct blkio throttle_io_serviced; // operations
151 +
152 + struct blkio io_merged; // operations
153 + struct blkio io_queued; // operations
154 +
155 + struct cgroup *next;
156 +
157 +} *cgroup_root = NULL;
158 +
159 +// ----------------------------------------------------------------------------
160 +// read values from /sys
161 +
162 +void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
163 + static procfile *ff = NULL;
164 +
165 + static uint32_t user_hash = 0;
166 + static uint32_t system_hash = 0;
167 +
168 + if(unlikely(user_hash == 0)) {
169 + user_hash = simple_hash("user");
170 + system_hash = simple_hash("system");
171 + }
172 +
173 + cp->updated = 0;
174 + if(cp->filename) {
175 + ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
176 + if(!ff) return;
177 +
178 + ff = procfile_readall(ff);
179 + if(!ff) return;
180 +
181 + unsigned long i, lines = procfile_lines(ff);
182 +
183 + if(lines < 1) {
184 + error("File '%s' should have 1+ lines.", cp->filename);
185 + return;
186 + }
187 +
188 + for(i = 0; i < lines ; i++) {
189 + char *s = procfile_lineword(ff, i, 0);
190 + uint32_t hash = simple_hash(s);
191 +
192 + if(hash == user_hash && !strcmp(s, "user"))
193 + cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
194 +
195 + else if(hash == system_hash && !strcmp(s, "system"))
196 + cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
197 + }
198 +
199 + cp->updated = 1;
200 +
201 + // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
202 + }
203 +}
204 +
205 +void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
206 + static procfile *ff = NULL;
207 +
208 + ca->updated = 0;
209 + if(ca->filename) {
210 + ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
211 + if(!ff) return;
212 +
213 + ff = procfile_readall(ff);
214 + if(!ff) return;
215 +
216 + if(procfile_lines(ff) < 1) {
217 + error("File '%s' should have 1+ lines but has %d.", ca->filename, procfile_lines(ff));
218 + return;
219 + }
220 +
221 + unsigned long i = procfile_linewords(ff, 0);
222 + if(i <= 0) return;
223 +
224 + // we may have 1 more CPU reported
225 + while(i > 0) {
226 + char *s = procfile_lineword(ff, 0, i - 1);
227 + if(!*s) i--;
228 + else break;
229 + }
230 +
231 + if(i != ca->cpus) {
232 + free(ca->cpu_percpu);
233 + }
234 + ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
235 + if(!ca->cpu_percpu)
236 + fatal("Cannot allocate memory (%z bytes)", sizeof(unsigned long long) * i);
237 +
238 + ca->cpus = i;
239 +
240 + for(i = 0; i < ca->cpus ;i++) {
241 + ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
242 + // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
243 + }
244 +
245 + ca->updated = 1;
246 + }
247 +}
248 +
249 +void cgroup_read_blkio(struct blkio *io) {
250 + static procfile *ff = NULL;
251 +
252 + static uint32_t Read_hash = 0;
253 + static uint32_t Write_hash = 0;
254 + static uint32_t Sync_hash = 0;
255 + static uint32_t Async_hash = 0;
256 + static uint32_t Total_hash = 0;
257 +
258 + if(unlikely(Read_hash == 0)) {
259 + Read_hash = simple_hash("Read");
260 + Write_hash = simple_hash("Write");
261 + Sync_hash = simple_hash("Sync");
262 + Async_hash = simple_hash("Async");
263 + Total_hash = simple_hash("Total");
264 + }
265 +
266 + io->updated = 0;
267 + if(io->filename) {
268 + ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
269 + if(!ff) return;
270 +
271 + ff = procfile_readall(ff);
272 + if(!ff) return;
273 +
274 + unsigned long i, lines = procfile_lines(ff);
275 +
276 + if(lines < 1) {
277 + error("File '%s' should have 1+ lines.", io->filename);
278 + return;
279 + }
280 +
281 + io->Read = 0;
282 + io->Write = 0;
283 + io->Sync = 0;
284 + io->Async = 0;
285 + io->Total = 0;
286 +
287 + for(i = 0; i < lines ; i++) {
288 + char *s = procfile_lineword(ff, i, 1);
289 + uint32_t hash = simple_hash(s);
290 +
291 + if(hash == Read_hash && !strcmp(s, "Read"))
292 + io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
293 +
294 + else if(hash == Write_hash && !strcmp(s, "Write"))
295 + io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
296 +
297 + else if(hash == Sync_hash && !strcmp(s, "Sync"))
298 + io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
299 +
300 + else if(hash == Async_hash && !strcmp(s, "Async"))
301 + io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
302 +
303 + else if(hash == Total_hash && !strcmp(s, "Total"))
304 + io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
305 + }
306 +
307 + io->updated = 1;
308 + // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
309 + }
310 +}
311 +
312 +void cgroup_read_memory(struct memory *mem) {
313 + static procfile *ff = NULL;
314 +
315 + static uint32_t cache_hash = 0;
316 + static uint32_t rss_hash = 0;
317 + static uint32_t rss_huge_hash = 0;
318 + static uint32_t mapped_file_hash = 0;
319 + static uint32_t writeback_hash = 0;
320 + static uint32_t pgpgin_hash = 0;
321 + static uint32_t pgpgout_hash = 0;
322 + static uint32_t pgfault_hash = 0;
323 + static uint32_t pgmajfault_hash = 0;
324 +/* static uint32_t inactive_anon_hash = 0;
325 + static uint32_t active_anon_hash = 0;
326 + static uint32_t inactive_file_hash = 0;
327 + static uint32_t active_file_hash = 0;
328 + static uint32_t unevictable_hash = 0;
329 + static uint32_t hierarchical_memory_limit_hash = 0;
330 + static uint32_t total_cache_hash = 0;
331 + static uint32_t total_rss_hash = 0;
332 + static uint32_t total_rss_huge_hash = 0;
333 + static uint32_t total_mapped_file_hash = 0;
334 + static uint32_t total_writeback_hash = 0;
335 + static uint32_t total_pgpgin_hash = 0;
336 + static uint32_t total_pgpgout_hash = 0;
337 + static uint32_t total_pgfault_hash = 0;
338 + static uint32_t total_pgmajfault_hash = 0;
339 + static uint32_t total_inactive_anon_hash = 0;
340 + static uint32_t total_active_anon_hash = 0;
341 + static uint32_t total_inactive_file_hash = 0;
342 + static uint32_t total_active_file_hash = 0;
343 + static uint32_t total_unevictable_hash = 0;
344 +*/
345 + if(unlikely(cache_hash == 0)) {
346 + cache_hash = simple_hash("cache");
347 + rss_hash = simple_hash("rss");
348 + rss_huge_hash = simple_hash("rss_huge");
349 + mapped_file_hash = simple_hash("mapped_file");
350 + writeback_hash = simple_hash("writeback");
351 + pgpgin_hash = simple_hash("pgpgin");
352 + pgpgout_hash = simple_hash("pgpgout");
353 + pgfault_hash = simple_hash("pgfault");
354 + pgmajfault_hash = simple_hash("pgmajfault");
355 +/* inactive_anon_hash = simple_hash("inactive_anon");
356 + active_anon_hash = simple_hash("active_anon");
357 + inactive_file_hash = simple_hash("inactive_file");
358 + active_file_hash = simple_hash("active_file");
359 + unevictable_hash = simple_hash("unevictable");
360 + hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
361 + total_cache_hash = simple_hash("total_cache");
362 + total_rss_hash = simple_hash("total_rss");
363 + total_rss_huge_hash = simple_hash("total_rss_huge");
364 + total_mapped_file_hash = simple_hash("total_mapped_file");
365 + total_writeback_hash = simple_hash("total_writeback");
366 + total_pgpgin_hash = simple_hash("total_pgpgin");
367 + total_pgpgout_hash = simple_hash("total_pgpgout");
368 + total_pgfault_hash = simple_hash("total_pgfault");
369 + total_pgmajfault_hash = simple_hash("total_pgmajfault");
370 + total_inactive_anon_hash = simple_hash("total_inactive_anon");
371 + total_active_anon_hash = simple_hash("total_active_anon");
372 + total_inactive_file_hash = simple_hash("total_inactive_file");
373 + total_active_file_hash = simple_hash("total_active_file");
374 + total_unevictable_hash = simple_hash("total_unevictable");
375 +*/
376 + }
377 +
378 + mem->updated = 0;
379 + if(mem->filename) {
380 + ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
381 + if(!ff) return;
382 +
383 + ff = procfile_readall(ff);
384 + if(!ff) return;
385 +
386 + unsigned long i, lines = procfile_lines(ff);
387 +
388 + if(lines < 1) {
389 + error("File '%s' should have 1+ lines.", mem->filename);
390 + return;
391 + }
392 +
393 + for(i = 0; i < lines ; i++) {
394 + char *s = procfile_lineword(ff, i, 0);
395 + uint32_t hash = simple_hash(s);
396 +
397 + if(hash == cache_hash && !strcmp(s, "cache"))
398 + mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
399 +
400 + else if(hash == rss_hash && !strcmp(s, "rss"))
401 + mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
402 +
403 + else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
404 + mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
405 +
406 + else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
407 + mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
408 +
409 + else if(hash == writeback_hash && !strcmp(s, "writeback"))
410 + mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
411 +
412 + else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
413 + mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
414 +
415 + else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
416 + mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
417 +
418 + else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
419 + mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
420 +
421 + else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
422 + mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
423 +
424 +/* else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
425 + mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
426 +
427 + else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
428 + mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
429 +
430 + else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
431 + mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
432 +
433 + else if(hash == active_file_hash && !strcmp(s, "active_file"))
434 + mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
435 +
436 + else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
437 + mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
438 +
439 + else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
440 + mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
441 +
442 + else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
443 + mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
444 +
445 + else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
446 + mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
447 +
448 + else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
449 + mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
450 +
451 + else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
452 + mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
453 +
454 + else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
455 + mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
456 +
457 + else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
458 + mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
459 +
460 + else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
461 + mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
462 +
463 + else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
464 + mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
465 +
466 + else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
467 + mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
468 +
469 + else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
470 + mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
471 +
472 + else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
473 + mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
474 +
475 + else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
476 + mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
477 +
478 + else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
479 + mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
480 +
481 + else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
482 + mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
483 +*/
484 + }
485 +
486 + // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
487 +
488 + mem->updated = 1;
489 + }
490 +}
491 +
492 +void cgroup_read(struct cgroup *cg) {
493 + cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
494 + cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
495 + cgroup_read_memory(&cg->memory);
496 + cgroup_read_blkio(&cg->io_service_bytes);
497 + cgroup_read_blkio(&cg->io_serviced);
498 + cgroup_read_blkio(&cg->throttle_io_service_bytes);
499 + cgroup_read_blkio(&cg->throttle_io_serviced);
500 + cgroup_read_blkio(&cg->io_merged);
501 + cgroup_read_blkio(&cg->io_queued);
502 +}
503 +
504 +void read_all_cgroups(struct cgroup *cg) {
505 + struct cgroup *i;
506 +
507 + for(i = cg; i ; i = i->next)
508 + cgroup_read(i);
509 +}
510 +
511 +// ----------------------------------------------------------------------------
512 +// add/remove/find cgroup objects
513 +
514 +struct cgroup *cgroup_add(const char *id) {
515 + if(cgroup_root_count >= cgroup_root_max) {
516 + info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
517 + return NULL;
518 + }
519 +
520 + int def = cgroup_enable_new_cgroups_detected_at_runtime;
521 + const char *name = id;
522 + if(!*name) {
523 + name = "/";
524 +
525 + // disable by default the host cgroup
526 + def = 0;
527 + }
528 + else {
529 + if(*name == '/') name++;
530 +
531 + // disable by default the parent cgroup
532 + // for known cgroup managers
533 + if(!strcmp(name, "lxc") || !strcmp(name, "docker"))
534 + def = 0;
535 + }
536 +
537 + char option[FILENAME_MAX + 1];
538 + snprintf(option, FILENAME_MAX, "enable cgroup %s", name);
539 + if(!config_get_boolean("plugin:cgroups", option, def))
540 + return NULL;
541 +
542 + struct cgroup *cg = calloc(1, sizeof(struct cgroup));
543 + if(cg) {
544 + cg->id = strdup(id);
545 + if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
546 +
547 + cg->hash = simple_hash(cg->id);
548 +
549 + cg->name = strdup(name);
550 + if(!cg->name) fatal("Cannot allocate memory for cgroup '%s'", id);
551 +
552 + if(!cgroup_root)
553 + cgroup_root = cg;
554 + else {
555 + // append it
556 + struct cgroup *e;
557 + for(e = cgroup_root; e->next ;e = e->next) ;
558 + e->next = cg;
559 + }
560 +
561 + cgroup_root_count++;
562 +
563 + // fprintf(stderr, " > added cgroup No %d, with id '%s' (%u) and name '%s'\n", cgroup_root_count, cg->id, cg->hash, cg->name);
564 + }
565 + else fatal("Cannot allocate memory for cgroup '%s'", id);
566 +
567 + return cg;
568 +}
569 +
570 +void cgroup_remove(struct cgroup *cg) {
571 + if(cg == cgroup_root) {
572 + cgroup_root = cg->next;
573 + }
574 + else {
575 + struct cgroup *e;
576 + for(e = cgroup_root; e->next ;e = e->next)
577 + if(unlikely(e->next == cg)) break;
578 +
579 + if(e->next != cg) {
580 + error("Cannot find cgroup '%s' in list of cgroups", cg->id);
581 + }
582 + else {
583 + e->next = cg->next;
584 + cg->next = NULL;
585 + }
586 + }
587 +
588 + free(cg->cpuacct_usage.cpu_percpu);
589 +
590 + free(cg->cpuacct_stat.filename);
591 + free(cg->cpuacct_usage.filename);
592 + free(cg->memory.filename);
593 + free(cg->io_service_bytes.filename);
594 + free(cg->io_serviced.filename);
595 + free(cg->throttle_io_service_bytes.filename);
596 + free(cg->throttle_io_serviced.filename);
597 + free(cg->io_merged.filename);
598 + free(cg->io_queued.filename);
599 +
600 + free(cg->id);
601 + free(cg->name);
602 + free(cg);
603 +}
604 +
605 +// find if a given cgroup exists
606 +struct cgroup *cgroup_find(const char *id) {
607 + uint32_t hash = simple_hash(id);
608 +
609 + // fprintf(stderr, " > searching for '%s' (%u)\n", id, hash);
610 +
611 + struct cgroup *cg;
612 + for(cg = cgroup_root; cg ; cg = cg->next) {
613 + if(hash == cg->hash && strcmp(id, cg->id) == 0)
614 + break;
615 + }
616 +
617 + return cg;
618 +}
619 +
620 +// ----------------------------------------------------------------------------
621 +// detect running cgroups
622 +
623 +// callback for find_file_in_subdirs()
624 +void found_dir_in_subdir(const char *dir) {
625 + // fprintf(stderr, "found dir '%s'\n", dir);
626 +
627 + struct cgroup *cg = cgroup_find(dir);
628 + if(!cg) {
629 + if(*dir && cgroup_max_depth > 0) {
630 + int depth = 0;
631 + const char *s;
632 +
633 + for(s = dir; *s ;s++)
634 + if(unlikely(*s == '/'))
635 + depth++;
636 +
637 + if(depth > cgroup_max_depth) {
638 + info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
639 + return;
640 + }
641 + }
642 + cg = cgroup_add(dir);
643 + }
644 +
645 + if(cg) cg->available = 1;
646 +}
647 +
648 +void find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
649 + if(!this) this = base;
650 + size_t dirlen = strlen(this), baselen = strlen(base);
651 +
652 + DIR *dir = opendir(this);
653 + if(!dir) return;
654 +
655 + callback(&this[baselen]);
656 +
657 + struct dirent *de = NULL;
658 + while((de = readdir(dir))) {
659 + if(de->d_type == DT_DIR
660 + && (
661 + (de->d_name[0] == '.' && de->d_name[1] == '\0')
662 + || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
663 + ))
664 + continue;
665 +
666 + // fprintf(stderr, "examining '%s/%s'\n", this, de->d_name);
667 +
668 + if(de->d_type == DT_DIR) {
669 + char *s = malloc(dirlen + strlen(de->d_name) + 2);
670 + if(s) {
671 + strcpy(s, this);
672 + strcat(s, "/");
673 + strcat(s, de->d_name);
674 + find_dir_in_subdirs(base, s, callback);
675 + free(s);
676 + }
677 + }
678 + }
679 +
680 + closedir(dir);
681 +}
682 +
683 +void mark_all_cgroups_as_not_available() {
684 + struct cgroup *cg;
685 +
686 + // mark all as not available
687 + for(cg = cgroup_root; cg ; cg = cg->next)
688 + cg->available = 0;
689 +}
690 +
691 +struct cgroup *find_all_cgroups() {
692 +
693 + mark_all_cgroups_as_not_available();
694 +
695 + if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
696 + find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_dir_in_subdir);
697 +
698 + if(cgroup_enable_blkio)
699 + find_dir_in_subdirs(cgroup_blkio_base, NULL, found_dir_in_subdir);
700 +
701 + if(cgroup_enable_memory)
702 + find_dir_in_subdirs(cgroup_memory_base, NULL, found_dir_in_subdir);
703 +
704 + struct cgroup *cg;
705 + for(cg = cgroup_root; cg ; cg = cg->next) {
706 + // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
707 +
708 + if(!cg->available) {
709 + cgroup_remove(cg);
710 + continue;
711 + }
712 +
713 + // check for newly added cgroups
714 + // and update the filenames they read
715 + char filename[FILENAME_MAX + 1];
716 + if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
717 + snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
718 + cg->cpuacct_stat.filename = strdup(filename);
719 + }
720 + if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
721 + snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
722 + cg->cpuacct_usage.filename = strdup(filename);
723 + }
724 + if(cgroup_enable_memory && !cg->memory.filename) {
725 + snprintf(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
726 + cg->memory.filename = strdup(filename);
727 + }
728 + if(cgroup_enable_blkio) {
729 + if(!cg->io_service_bytes.filename) {
730 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
731 + cg->io_service_bytes.filename = strdup(filename);
732 + }
733 + if(!cg->io_serviced.filename) {
734 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
735 + cg->io_serviced.filename = strdup(filename);
736 + }
737 + if(!cg->throttle_io_service_bytes.filename) {
738 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
739 + cg->throttle_io_service_bytes.filename = strdup(filename);
740 + }
741 + if(!cg->throttle_io_serviced.filename) {
742 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
743 + cg->throttle_io_serviced.filename = strdup(filename);
744 + }
745 + if(!cg->io_merged.filename) {
746 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
747 + cg->io_merged.filename = strdup(filename);
748 + }
749 + if(!cg->io_queued.filename) {
750 + snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
751 + cg->io_queued.filename = strdup(filename);
752 + }
753 + }
754 + }
755 +
756 + return cgroup_root;
757 +}
758 +
759 +// ----------------------------------------------------------------------------
760 +// generate charts
761 +
762 +#define CHART_TITLE_MAX 300
763 +
764 +void update_cgroup_charts(int update_every) {
765 + char type[RRD_ID_LENGTH_MAX + 1];
766 + char title[CHART_TITLE_MAX + 1];
767 +
768 + struct cgroup *cg;
769 + RRDSET *st;
770 +
771 + for(cg = cgroup_root; cg ; cg = cg->next) {
772 + if(!cg->available) continue;
773 +
774 + if(cg->id[0] == '\0')
775 + strcpy(type, "cgroup_host");
776 + else if(cg->id[0] == '/')
777 + snprintf(type, RRD_ID_LENGTH_MAX, "cgroup%s", cg->id);
778 + else
779 + snprintf(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->id);
780 +
781 + netdata_fix_chart_id(type);
782 +
783 + if(cg->cpuacct_stat.updated) {
784 + st = rrdset_find_bytype(type, "cpu");
785 + if(!st) {
786 + snprintf(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->name);
787 + st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
788 +
789 + rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
790 + rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
791 + }
792 + else rrdset_next(st);
793 +
794 + rrddim_set(st, "user", cg->cpuacct_stat.user);
795 + rrddim_set(st, "system", cg->cpuacct_stat.system);
796 + rrdset_done(st);
797 + }
798 +
799 + if(cg->cpuacct_usage.updated) {
800 + char id[RRD_ID_LENGTH_MAX + 1];
801 + unsigned int i;
802 +
803 + st = rrdset_find_bytype(type, "cpu_per_core");
804 + if(!st) {
805 + snprintf(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->name);
806 + st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
807 +
808 + for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
809 + snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
810 + rrddim_add(st, id, NULL, 100, 1000000, RRDDIM_INCREMENTAL);
811 + }
812 + }
813 + else rrdset_next(st);
814 +
815 + for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
816 + snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
817 + rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
818 + }
819 + rrdset_done(st);
820 + }
821 +
822 + if(cg->memory.updated) {
823 + if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
824 + st = rrdset_find_bytype(type, "mem");
825 + if(!st) {
826 + snprintf(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->name);
827 + st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
828 + RRDSET_TYPE_STACKED);
829 +
830 + rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
831 + rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
832 + rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
833 + rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
834 + }
835 + else rrdset_next(st);
836 +
837 + rrddim_set(st, "cache", cg->memory.cache);
838 + rrddim_set(st, "rss", cg->memory.rss);
839 + rrddim_set(st, "rss_huge", cg->memory.rss_huge);
840 + rrddim_set(st, "mapped_file", cg->memory.mapped_file);
841 + rrdset_done(st);
842 + }
843 +
844 + st = rrdset_find_bytype(type, "writeback");
845 + if(!st) {
846 + snprintf(title, CHART_TITLE_MAX, "Writable Memory for cgroup %s", cg->name);
847 + st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
848 + update_every, RRDSET_TYPE_AREA);
849 +
850 + rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
851 + }
852 + else rrdset_next(st);
853 +
854 + rrddim_set(st, "writeback", cg->memory.writeback);
855 + rrdset_done(st);
856 +
857 + if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
858 + st = rrdset_find_bytype(type, "mem_activity");
859 + if(!st) {
860 + snprintf(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->name);
861 + st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
862 + 40400, update_every, RRDSET_TYPE_LINE);
863 +
864 + rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
865 + rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
866 + }
867 + else rrdset_next(st);
868 +
869 + rrddim_set(st, "pgpgin", cg->memory.pgpgin);
870 + rrddim_set(st, "pgpgout", cg->memory.pgpgout);
871 + rrdset_done(st);
872 + }
873 +
874 + if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
875 + st = rrdset_find_bytype(type, "pgfaults");
876 + if(!st) {
877 + snprintf(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->name);
878 + st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
879 + update_every, RRDSET_TYPE_LINE);
880 +
881 + rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
882 + rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
883 + }
884 + else rrdset_next(st);
885 +
886 + rrddim_set(st, "pgfault", cg->memory.pgfault);
887 + rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
888 + rrdset_done(st);
889 + }
890 + }
891 +
892 + if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
893 + st = rrdset_find_bytype(type, "io");
894 + if(!st) {
895 + snprintf(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->name);
896 + st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
897 + update_every, RRDSET_TYPE_LINE);
898 +
899 + rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
900 + rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
901 + }
902 + else rrdset_next(st);
903 +
904 + rrddim_set(st, "read", cg->io_service_bytes.Read);
905 + rrddim_set(st, "write", cg->io_service_bytes.Write);
906 + rrdset_done(st);
907 + }
908 +
909 + if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
910 + st = rrdset_find_bytype(type, "serviced_ops");
911 + if(!st) {
912 + snprintf(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->name);
913 + st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
914 + update_every, RRDSET_TYPE_LINE);
915 +
916 + rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
917 + rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
918 + }
919 + else rrdset_next(st);
920 +
921 + rrddim_set(st, "read", cg->io_serviced.Read);
922 + rrddim_set(st, "write", cg->io_serviced.Write);
923 + rrdset_done(st);
924 + }
925 +
926 + if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
927 + st = rrdset_find_bytype(type, "io");
928 + if(!st) {
929 + snprintf(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->name);
930 + st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
931 + update_every, RRDSET_TYPE_LINE);
932 +
933 + rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
934 + rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
935 + }
936 + else rrdset_next(st);
937 +
938 + rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
939 + rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
940 + rrdset_done(st);
941 + }
942 +
943 +
944 + if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
945 + st = rrdset_find_bytype(type, "throttle_serviced_ops");
946 + if(!st) {
947 + snprintf(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->name);
948 + st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
949 + update_every, RRDSET_TYPE_LINE);
950 +
951 + rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
952 + rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
953 + }
954 + else rrdset_next(st);
955 +
956 + rrddim_set(st, "read", cg->throttle_io_serviced.Read);
957 + rrddim_set(st, "write", cg->throttle_io_serviced.Write);
958 + rrdset_done(st);
959 + }
960 +
961 + if(cg->io_queued.updated) {
962 + st = rrdset_find_bytype(type, "queued_ops");
963 + if(!st) {
964 + snprintf(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->name);
965 + st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
966 + update_every, RRDSET_TYPE_LINE);
967 +
968 + rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
969 + rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
970 + }
971 + else rrdset_next(st);
972 +
973 + rrddim_set(st, "read", cg->io_queued.Read);
974 + rrddim_set(st, "write", cg->io_queued.Write);
975 + rrdset_done(st);
976 + }
977 +
978 + if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
979 + st = rrdset_find_bytype(type, "merged_ops");
980 + if(!st) {
981 + snprintf(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->name);
982 + st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
983 + update_every, RRDSET_TYPE_LINE);
984 +
985 + rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
986 + rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
987 + }
988 + else rrdset_next(st);
989 +
990 + rrddim_set(st, "read", cg->io_merged.Read);
991 + rrddim_set(st, "write", cg->io_merged.Write);
992 + rrdset_done(st);
993 + }
994 + }
995 +}
996 +
997 +// ----------------------------------------------------------------------------
998 +// cgroups main
999 +
1000 +int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
1001 + static int cgroup_global_config_read = 0;
1002 + static time_t last_run = 0;
1003 + time_t now = time(NULL);
1004 +
1005 + if(dt) {};
1006 +
1007 + if(unlikely(!cgroup_global_config_read)) {
1008 + read_cgroup_plugin_configuration();
1009 + cgroup_global_config_read = 1;
1010 + }
1011 +
1012 + if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
1013 + find_all_cgroups();
1014 + last_run = now;
1015 + }
1016 +
1017 + read_all_cgroups(cgroup_root);
1018 + update_cgroup_charts(update_every);
1019 +
1020 + return 0;
1021 +}
web/index.html
+32 -3
@@ -1012,6 +1012,21 @@ var menuData = {
1012 title: 'Example Charts',
1013 info: undefined
1014 },
1015 +
1016 + 'cgroup': {
1017 + title: 'Container',
1018 + info: undefined
1019 + },
1020 +
1021 + 'mysql': {
1022 + title: 'MySQL',
1023 + info: undefined
1024 + },
1025 +
1026 + 'named': {
1027 + title: 'named',
1028 + info: undefined
1029 + },
1030 };
1031
1032 var submenuData = {
@@ -1322,8 +1337,13 @@ function anyAttribute(obj, attr, key, def) {
1337 return def;
1338 }
1339
1325 -function menuTitle(menu) {
1326 - return anyAttribute(menuData, 'title', menu, menu);
1340 +function menuTitle(chart) {
1341 + if(typeof chart.menu_pattern !== 'undefined') {
1342 + return anyAttribute(menuData, 'title', chart.menu_pattern, chart.menu_pattern).toString()
1343 + + ': ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString();
1344 + }
1345 +
1346 + return anyAttribute(menuData, 'title', chart.menu, chart.menu);
1347 }
1348
1349 function menuInfo(menu) {
@@ -1379,6 +1399,13 @@ function enrichChartData(chart) {
1399 chart.menu = tmp;
1400 break;
1401
1402 + case 'mysql':
1403 + case 'named':
1404 + case 'cgroup':
1405 + chart.menu = chart.type;
1406 + chart.menu_pattern = tmp;
1407 + break;
1408 +
1409 case 'tc':
1410 chart.menu = tmp;
1411
@@ -1654,7 +1681,7 @@ function renderChartsAndMenu(data) {
1681 menus[charts[c].menu] = {
1682 priority: charts[c].priority,
1683 submenus: {},
1657 - title: menuTitle(charts[c].menu),
1684 + title: menuTitle(charts[c]),
1685 info: menuInfo(charts[c].menu),
1686 height: menuHeight(charts[c].menu, options.chartsHeight)
1687 };
@@ -1681,6 +1708,8 @@ function renderChartsAndMenu(data) {
1708 menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]);
1709 }
1710
1711 + // propagate the descriptive subname given to QoS
1712 + // to all the other submenus with the same name
1713 for(var m in menus) {
1714 for(var s in menus[m].submenus) {
1715 // set the family using a name