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