]> arthur.barton.de Git - netdata.git/blob - src/sys_fs_cgroup.c
simple_pattern now also supports asterisks in the middle; cgroups cleanup;
[netdata.git] / src / sys_fs_cgroup.c
1 #include "common.h"
2
3 // ----------------------------------------------------------------------------
4 // cgroup globals
5
6 static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
7 static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
8 static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
9 static int cgroup_enable_devices = CONFIG_ONDEMAND_ONDEMAND;
10 static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
11 static int cgroup_enable_systemd_services = CONFIG_ONDEMAND_NO;
12 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
13 static int cgroup_check_for_new_every = 10;
14 static int cgroup_update_every = 1;
15 static char *cgroup_cpuacct_base = NULL;
16 static char *cgroup_blkio_base = NULL;
17 static char *cgroup_memory_base = NULL;
18 static char *cgroup_devices_base = NULL;
19
20 static int cgroup_root_count = 0;
21 static int cgroup_root_max = 500;
22 static int cgroup_max_depth = 0;
23
24 static NETDATA_SIMPLE_PATTERN *disabled_cgroups_patterns = NULL;
25 static NETDATA_SIMPLE_PATTERN *disabled_cgroup_paths = NULL;
26 static NETDATA_SIMPLE_PATTERN *disabled_cgroup_renames = NULL;
27 static NETDATA_SIMPLE_PATTERN *systemd_services_cgroups = NULL;
28
29 static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh";
30
31 void read_cgroup_plugin_configuration() {
32     cgroup_update_every = (int)config_get_number("plugin:cgroups", "update every", rrd_update_every);
33     if(cgroup_update_every < rrd_update_every)
34         cgroup_update_every = rrd_update_every;
35
36     cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every * cgroup_update_every);
37     if(cgroup_check_for_new_every < cgroup_update_every)
38         cgroup_check_for_new_every = cgroup_update_every;
39
40     cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
41     cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
42     cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
43     cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
44
45     cgroup_enable_systemd_services = config_get_boolean_ondemand("plugin:cgroups", "enable systemd services", cgroup_enable_systemd_services);
46
47     char filename[FILENAME_MAX + 1], *s;
48     struct mountinfo *mi, *root = mountinfo_read(0);
49
50     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
51     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
52     if(!mi) {
53         error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
54         s = "/sys/fs/cgroup/cpuacct";
55     }
56     else s = mi->mount_point;
57     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
58     cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
59
60     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
61     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
62     if(!mi) {
63         error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
64         s = "/sys/fs/cgroup/blkio";
65     }
66     else s = mi->mount_point;
67     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
68     cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
69
70     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
71     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
72     if(!mi) {
73         error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
74         s = "/sys/fs/cgroup/memory";
75     }
76     else s = mi->mount_point;
77     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
78     cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
79
80     mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
81     if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
82     if(!mi) {
83         error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
84         s = "/sys/fs/cgroup/devices";
85     }
86     else s = mi->mount_point;
87     snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
88     cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
89
90     cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
91     cgroup_max_depth = (int)config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
92
93     cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
94
95     disabled_cgroups_patterns = netdata_simple_pattern_list_create(
96             config_get("plugin:cgroups", "disable by default cgroups matching",
97                     " / /lxc /docker /libvirt /qemu /systemd "
98                     " /system /system.slice "
99                     " /machine /machine.slice "
100                     " /user /user.slice "
101                     " /init.scope "
102                     " *.swap "
103                     " *.slice "
104                     " *.user "
105                     " *.mount "
106                     " *.partition "
107                     " *.service "
108                     " */ns "                               //   /lxc/*/ns    #1397
109             ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
110
111     disabled_cgroup_paths = netdata_simple_pattern_list_create(
112             config_get("plugin:cgroups", "do not search for cgroups in paths matching",
113                     " /system "
114                     " /systemd "
115                     " /user.slice "
116                     " /user "
117                     " /init.scope "
118                     " *-qemu "                             //  #345
119             ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
120
121     cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", cgroups_rename_script);
122
123     disabled_cgroup_renames = netdata_simple_pattern_list_create(
124             config_get("plugin:cgroups", "do not run script to rename cgroups matching",
125                     " / "
126                     " *.service "
127                     " *.slice "
128                     " *.scope "
129                     " *.swap "
130                     " *.mount "
131                     " *.partition "
132                     " *.user "
133             ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
134
135     if(cgroup_enable_systemd_services)
136         systemd_services_cgroups = netdata_simple_pattern_list_create(
137                 config_get("plugin:cgroups", "cgroups to match as systemd services",
138                         " /system.slice/*.service "
139                 ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
140
141     mountinfo_free(root);
142 }
143
144 // ----------------------------------------------------------------------------
145 // cgroup objects
146
147 struct blkio {
148     int updated;
149
150     char *filename;
151
152     unsigned long long Read;
153     unsigned long long Write;
154 /*
155     unsigned long long Sync;
156     unsigned long long Async;
157     unsigned long long Total;
158 */
159 };
160
161 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
162 struct memory {
163     int updated;
164
165     char *filename;
166
167     int has_dirty_swap;
168
169     unsigned long long cache;
170     unsigned long long rss;
171     unsigned long long rss_huge;
172     unsigned long long mapped_file;
173     unsigned long long writeback;
174     unsigned long long dirty;
175     unsigned long long swap;
176     unsigned long long pgpgin;
177     unsigned long long pgpgout;
178     unsigned long long pgfault;
179     unsigned long long pgmajfault;
180 /*
181     unsigned long long inactive_anon;
182     unsigned long long active_anon;
183     unsigned long long inactive_file;
184     unsigned long long active_file;
185     unsigned long long unevictable;
186     unsigned long long hierarchical_memory_limit;
187     unsigned long long total_cache;
188     unsigned long long total_rss;
189     unsigned long long total_rss_huge;
190     unsigned long long total_mapped_file;
191     unsigned long long total_writeback;
192     unsigned long long total_dirty;
193     unsigned long long total_swap;
194     unsigned long long total_pgpgin;
195     unsigned long long total_pgpgout;
196     unsigned long long total_pgfault;
197     unsigned long long total_pgmajfault;
198     unsigned long long total_inactive_anon;
199     unsigned long long total_active_anon;
200     unsigned long long total_inactive_file;
201     unsigned long long total_active_file;
202     unsigned long long total_unevictable;
203 */
204
205     int usage_in_bytes_updated;
206     char *filename_usage_in_bytes;
207     unsigned long long usage_in_bytes;
208
209     int msw_usage_in_bytes_updated;
210     char *filename_msw_usage_in_bytes;
211     unsigned long long msw_usage_in_bytes;
212
213     int failcnt_updated;
214     char *filename_failcnt;
215     unsigned long long failcnt;
216 };
217
218 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
219 struct cpuacct_stat {
220     int updated;
221
222     char *filename;
223
224     unsigned long long user;
225     unsigned long long system;
226 };
227
228 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
229 struct cpuacct_usage {
230     int updated;
231
232     char *filename;
233
234     unsigned int cpus;
235     unsigned long long *cpu_percpu;
236 };
237
238 #define CGROUP_OPTIONS_DISABLED_DUPLICATE   0x00000001
239 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
240
241 struct cgroup {
242     uint32_t options;
243
244     char available;      // found in the filesystem
245     char enabled;        // enabled in the config
246
247     char *id;
248     uint32_t hash;
249
250     char *chart_id;
251     uint32_t hash_chart;
252
253     char *chart_title;
254
255     struct cpuacct_stat cpuacct_stat;
256     struct cpuacct_usage cpuacct_usage;
257
258     struct memory memory;
259
260     struct blkio io_service_bytes;              // bytes
261     struct blkio io_serviced;                   // operations
262
263     struct blkio throttle_io_service_bytes;     // bytes
264     struct blkio throttle_io_serviced;          // operations
265
266     struct blkio io_merged;                     // operations
267     struct blkio io_queued;                     // operations
268
269     RRDSET *st_cpu;
270     RRDSET *st_cpu_per_core;
271     RRDSET *st_mem;
272     RRDSET *st_writeback;
273     RRDSET *st_mem_activity;
274     RRDSET *st_pgfaults;
275     RRDSET *st_mem_usage;
276     RRDSET *st_mem_failcnt;
277     RRDSET *st_io;
278     RRDSET *st_serviced_ops;
279     RRDSET *st_throttle_io;
280     RRDSET *st_throttle_serviced_ops;
281     RRDSET *st_queued_ops;
282     RRDSET *st_merged_ops;
283
284     struct cgroup *next;
285
286 } *cgroup_root = NULL;
287
288 // ----------------------------------------------------------------------------
289 // read values from /sys
290
291 void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
292     static procfile *ff = NULL;
293
294     static uint32_t user_hash = 0;
295     static uint32_t system_hash = 0;
296
297     if(unlikely(user_hash == 0)) {
298         user_hash = simple_hash("user");
299         system_hash = simple_hash("system");
300     }
301
302     cp->updated = 0;
303     if(cp->filename) {
304         ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
305         if(!ff) return;
306
307         ff = procfile_readall(ff);
308         if(!ff) return;
309
310         unsigned long i, lines = procfile_lines(ff);
311
312         if(lines < 1) {
313             error("File '%s' should have 1+ lines.", cp->filename);
314             return;
315         }
316
317         for(i = 0; i < lines ; i++) {
318             char *s = procfile_lineword(ff, i, 0);
319             uint32_t hash = simple_hash(s);
320
321             if(hash == user_hash && !strcmp(s, "user"))
322                 cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
323
324             else if(hash == system_hash && !strcmp(s, "system"))
325                 cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
326         }
327
328         cp->updated = 1;
329
330         // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
331     }
332 }
333
334 void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
335     static procfile *ff = NULL;
336
337     ca->updated = 0;
338     if(ca->filename) {
339         ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
340         if(!ff) return;
341
342         ff = procfile_readall(ff);
343         if(!ff) return;
344
345         if(procfile_lines(ff) < 1) {
346             error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
347             return;
348         }
349
350         unsigned long i = procfile_linewords(ff, 0);
351         if(i == 0) return;
352
353         // we may have 1 more CPU reported
354         while(i > 0) {
355             char *s = procfile_lineword(ff, 0, i - 1);
356             if(!*s) i--;
357             else break;
358         }
359
360         if(i != ca->cpus) {
361             freez(ca->cpu_percpu);
362             ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
363             ca->cpus = (unsigned int)i;
364         }
365
366         for(i = 0; i < ca->cpus ;i++) {
367             ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
368             // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
369         }
370
371         ca->updated = 1;
372     }
373 }
374
375 void cgroup_read_blkio(struct blkio *io) {
376     static procfile *ff = NULL;
377
378     static uint32_t Read_hash = 0;
379     static uint32_t Write_hash = 0;
380 /*
381     static uint32_t Sync_hash = 0;
382     static uint32_t Async_hash = 0;
383     static uint32_t Total_hash = 0;
384 */
385
386     if(unlikely(Read_hash == 0)) {
387         Read_hash = simple_hash("Read");
388         Write_hash = simple_hash("Write");
389 /*
390         Sync_hash = simple_hash("Sync");
391         Async_hash = simple_hash("Async");
392         Total_hash = simple_hash("Total");
393 */
394     }
395
396     io->updated = 0;
397     if(io->filename) {
398         ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
399         if(!ff) return;
400
401         ff = procfile_readall(ff);
402         if(!ff) return;
403
404         unsigned long i, lines = procfile_lines(ff);
405
406         if(lines < 1) {
407             error("File '%s' should have 1+ lines.", io->filename);
408             return;
409         }
410
411         io->Read = 0;
412         io->Write = 0;
413 /*
414         io->Sync = 0;
415         io->Async = 0;
416         io->Total = 0;
417 */
418
419         for(i = 0; i < lines ; i++) {
420             char *s = procfile_lineword(ff, i, 1);
421             uint32_t hash = simple_hash(s);
422
423             if(hash == Read_hash && !strcmp(s, "Read"))
424                 io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
425
426             else if(hash == Write_hash && !strcmp(s, "Write"))
427                 io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
428
429 /*
430             else if(hash == Sync_hash && !strcmp(s, "Sync"))
431                 io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
432
433             else if(hash == Async_hash && !strcmp(s, "Async"))
434                 io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
435
436             else if(hash == Total_hash && !strcmp(s, "Total"))
437                 io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
438 */
439         }
440
441         io->updated = 1;
442         // 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);
443     }
444 }
445
446 void cgroup_read_memory(struct memory *mem) {
447     static procfile *ff = NULL;
448
449     static uint32_t cache_hash = 0;
450     static uint32_t rss_hash = 0;
451     static uint32_t rss_huge_hash = 0;
452     static uint32_t mapped_file_hash = 0;
453     static uint32_t writeback_hash = 0;
454     static uint32_t dirty_hash = 0;
455     static uint32_t swap_hash = 0;
456     static uint32_t pgpgin_hash = 0;
457     static uint32_t pgpgout_hash = 0;
458     static uint32_t pgfault_hash = 0;
459     static uint32_t pgmajfault_hash = 0;
460 /*
461     static uint32_t inactive_anon_hash = 0;
462     static uint32_t active_anon_hash = 0;
463     static uint32_t inactive_file_hash = 0;
464     static uint32_t active_file_hash = 0;
465     static uint32_t unevictable_hash = 0;
466     static uint32_t hierarchical_memory_limit_hash = 0;
467     static uint32_t total_cache_hash = 0;
468     static uint32_t total_rss_hash = 0;
469     static uint32_t total_rss_huge_hash = 0;
470     static uint32_t total_mapped_file_hash = 0;
471     static uint32_t total_writeback_hash = 0;
472     static uint32_t total_dirty_hash = 0;
473     static uint32_t total_swap_hash = 0;
474     static uint32_t total_pgpgin_hash = 0;
475     static uint32_t total_pgpgout_hash = 0;
476     static uint32_t total_pgfault_hash = 0;
477     static uint32_t total_pgmajfault_hash = 0;
478     static uint32_t total_inactive_anon_hash = 0;
479     static uint32_t total_active_anon_hash = 0;
480     static uint32_t total_inactive_file_hash = 0;
481     static uint32_t total_active_file_hash = 0;
482     static uint32_t total_unevictable_hash = 0;
483 */
484     if(unlikely(cache_hash == 0)) {
485         cache_hash = simple_hash("cache");
486         rss_hash = simple_hash("rss");
487         rss_huge_hash = simple_hash("rss_huge");
488         mapped_file_hash = simple_hash("mapped_file");
489         writeback_hash = simple_hash("writeback");
490         dirty_hash = simple_hash("dirty");
491         swap_hash = simple_hash("swap");
492         pgpgin_hash = simple_hash("pgpgin");
493         pgpgout_hash = simple_hash("pgpgout");
494         pgfault_hash = simple_hash("pgfault");
495         pgmajfault_hash = simple_hash("pgmajfault");
496 /*
497         inactive_anon_hash = simple_hash("inactive_anon");
498         active_anon_hash = simple_hash("active_anon");
499         inactive_file_hash = simple_hash("inactive_file");
500         active_file_hash = simple_hash("active_file");
501         unevictable_hash = simple_hash("unevictable");
502         hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
503         total_cache_hash = simple_hash("total_cache");
504         total_rss_hash = simple_hash("total_rss");
505         total_rss_huge_hash = simple_hash("total_rss_huge");
506         total_mapped_file_hash = simple_hash("total_mapped_file");
507         total_writeback_hash = simple_hash("total_writeback");
508         total_dirty_hash = simple_hash("total_dirty");
509         total_swap_hash = simple_hash("total_swap");
510         total_pgpgin_hash = simple_hash("total_pgpgin");
511         total_pgpgout_hash = simple_hash("total_pgpgout");
512         total_pgfault_hash = simple_hash("total_pgfault");
513         total_pgmajfault_hash = simple_hash("total_pgmajfault");
514         total_inactive_anon_hash = simple_hash("total_inactive_anon");
515         total_active_anon_hash = simple_hash("total_active_anon");
516         total_inactive_file_hash = simple_hash("total_inactive_file");
517         total_active_file_hash = simple_hash("total_active_file");
518         total_unevictable_hash = simple_hash("total_unevictable");
519 */
520     }
521
522     mem->updated = 0;
523     if(mem->filename) {
524         ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
525         if(!ff) return;
526
527         ff = procfile_readall(ff);
528         if(!ff) return;
529
530         unsigned long i, lines = procfile_lines(ff);
531
532         if(lines < 1) {
533             error("File '%s' should have 1+ lines.", mem->filename);
534             return;
535         }
536
537         for(i = 0; i < lines ; i++) {
538             char *s = procfile_lineword(ff, i, 0);
539             uint32_t hash = simple_hash(s);
540
541             if(hash == cache_hash && !strcmp(s, "cache"))
542                 mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
543
544             else if(hash == rss_hash && !strcmp(s, "rss"))
545                 mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
546
547             else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
548                 mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
549
550             else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
551                 mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
552
553             else if(hash == writeback_hash && !strcmp(s, "writeback"))
554                 mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
555
556             else if(hash == dirty_hash && !strcmp(s, "dirty")) {
557                 mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
558                 mem->has_dirty_swap = 1;
559             }
560
561             else if(hash == swap_hash && !strcmp(s, "swap")) {
562                 mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
563                 mem->has_dirty_swap = 1;
564             }
565
566             else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
567                 mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
568
569             else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
570                 mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
571
572             else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
573                 mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
574
575             else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
576                 mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
577
578 /*
579             else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
580                 mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
581
582             else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
583                 mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
584
585             else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
586                 mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
587
588             else if(hash == active_file_hash && !strcmp(s, "active_file"))
589                 mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
590
591             else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
592                 mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
593
594             else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
595                 mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
596
597             else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
598                 mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
599
600             else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
601                 mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
602
603             else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
604                 mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
605
606             else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
607                 mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
608
609             else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
610                 mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
611
612             else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
613                 mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
614
615             else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
616                 mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
617
618             else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
619                 mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
620
621             else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
622                 mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
623
624             else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
625                 mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
626
627             else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
628                 mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
629
630             else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
631                 mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
632
633             else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
634                 mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
635
636             else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
637                 mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
638
639             else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
640                 mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
641
642             else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
643                 mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
644 */
645         }
646
647         // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %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_dirty: %llu, total_swap: %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->dirty, mem->swap, 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_dirty, mem->total_swap, 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);
648
649         mem->updated = 1;
650     }
651
652     mem->usage_in_bytes_updated = 0;
653     if(mem->filename_usage_in_bytes) {
654         if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes)))
655             mem->usage_in_bytes_updated = 1;
656     }
657
658     mem->msw_usage_in_bytes_updated = 0;
659     if(mem->filename_msw_usage_in_bytes) {
660         if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes)))
661             mem->msw_usage_in_bytes_updated = 1;
662     }
663
664     mem->failcnt_updated = 0;
665     if(mem->filename_failcnt) {
666         if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt)))
667             mem->failcnt_updated = 1;
668     }
669 }
670
671 void cgroup_read(struct cgroup *cg) {
672     debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
673
674     cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
675     cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
676     cgroup_read_memory(&cg->memory);
677     cgroup_read_blkio(&cg->io_service_bytes);
678     cgroup_read_blkio(&cg->io_serviced);
679     cgroup_read_blkio(&cg->throttle_io_service_bytes);
680     cgroup_read_blkio(&cg->throttle_io_serviced);
681     cgroup_read_blkio(&cg->io_merged);
682     cgroup_read_blkio(&cg->io_queued);
683 }
684
685 void read_all_cgroups(struct cgroup *root) {
686     debug(D_CGROUP, "reading metrics for all cgroups");
687
688     struct cgroup *cg;
689
690     for(cg = root; cg ; cg = cg->next)
691         if(cg->enabled && cg->available)
692             cgroup_read(cg);
693 }
694
695 // ----------------------------------------------------------------------------
696 // add/remove/find cgroup objects
697
698 #define CGROUP_CHARTID_LINE_MAX 1024
699
700 static char *cgroup_title_strdupz(const char *s) {
701     if(!s || !*s) s = "/";
702
703     if(*s == '/' && s[1] != '\0') s++;
704
705     char *r = strdupz(s);
706     netdata_fix_chart_name(r);
707
708     return r;
709 }
710
711 static char *cgroup_chart_id_strdupz(const char *s) {
712     if(!s || !*s) s = "/";
713
714     if(*s == '/' && s[1] != '\0') s++;
715
716     char *r = strdupz(s);
717     netdata_fix_chart_id(r);
718
719     return r;
720 }
721
722 void cgroup_get_chart_name(struct cgroup *cg) {
723     debug(D_CGROUP, "looking for the name of cgroup '%s' with chart id '%s' and title '%s'", cg->id, cg->chart_id, cg->chart_title);
724
725     if(!netdata_simple_pattern_list_matches(disabled_cgroup_renames, cg->id) &&
726             !netdata_simple_pattern_list_matches(disabled_cgroup_renames, cg->chart_id)) {
727
728         pid_t cgroup_pid;
729         char buffer[CGROUP_CHARTID_LINE_MAX + 1];
730
731         snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", cgroups_rename_script, cg->chart_id);
732
733         debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
734         FILE *fp = mypopen(buffer, &cgroup_pid);
735         if(fp) {
736             // debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
737             char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
738             // debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
739             mypclose(fp, cgroup_pid);
740             // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
741
742             if(s && *s && *s != '\n') {
743                 debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
744
745                 trim(s);
746
747                 freez(cg->chart_title);
748                 cg->chart_title = cgroup_title_strdupz(s);
749
750                 freez(cg->chart_id);
751                 cg->chart_id = cgroup_chart_id_strdupz(s);
752                 cg->hash_chart = simple_hash(cg->chart_id);
753             }
754         }
755         else
756             error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
757
758         debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
759     }
760     else
761         debug(D_CGROUP, "cgroup '%s' will not be renamed - it matches the list of disabled cgroup renames (will be shown as '%s')", cg->id, cg->chart_id);
762
763
764     if(cgroup_enable_systemd_services) {
765         if(netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->id) ||
766            netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->chart_id)) {
767             debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
768
769             char buffer[CGROUP_CHARTID_LINE_MAX + 1];
770             cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
771
772             strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
773             char *s = buffer;
774
775             // skip to the last slash
776             size_t len = strlen(s);
777             while(len--) if(unlikely(s[len] == '/')) break;
778             if(len) s = &s[len + 1];
779
780             // remove extension
781             //len = strlen(s);
782             //while(len--) if(unlikely(s[len] == '.')) break;
783             //if(len) s[len] = '\0';
784
785             freez(cg->chart_title);
786             cg->chart_title = cgroup_title_strdupz(s);
787
788             freez(cg->chart_id);
789             cg->chart_id = cgroup_chart_id_strdupz(s);
790             cg->hash_chart = simple_hash(cg->chart_id);
791             cg->enabled = 1;
792
793             debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
794         }
795         else
796             debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') does not match systemd services groups", cg->id, cg->chart_id, cg->chart_title);
797     }
798 }
799
800 struct cgroup *cgroup_add(const char *id) {
801     if(!id || !*id) id = "/";
802     debug(D_CGROUP, "adding to list, cgroup with id '%s'", id);
803
804     if(cgroup_root_count >= cgroup_root_max) {
805         info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
806         return NULL;
807     }
808
809     int def = netdata_simple_pattern_list_matches(disabled_cgroups_patterns, id)?0:cgroup_enable_new_cgroups_detected_at_runtime;
810     struct cgroup *cg = callocz(1, sizeof(struct cgroup));
811
812     cg->id = strdupz(id);
813     cg->hash = simple_hash(cg->id);
814
815     cg->chart_title = cgroup_title_strdupz(id);
816
817     cg->chart_id = cgroup_chart_id_strdupz(id);
818     cg->hash_chart = simple_hash(cg->chart_id);
819
820     if(!cgroup_root)
821         cgroup_root = cg;
822     else {
823         // append it
824         struct cgroup *e;
825         for(e = cgroup_root; e->next ;e = e->next) ;
826         e->next = cg;
827     }
828
829     cgroup_root_count++;
830
831     // fix the name by calling the external script
832     cgroup_get_chart_name(cg);
833
834     char option[FILENAME_MAX + 1];
835     snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
836     cg->enabled = (char)config_get_boolean("plugin:cgroups", option, def);
837
838     if(cg->enabled) {
839         struct cgroup *t;
840         for (t = cgroup_root; t; t = t->next) {
841             if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
842                 if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
843                     error("Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
844                           cg->chart_id, t->id, cg->id, t->id);
845                     debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
846                           cg->chart_id, t->id, cg->id, t->id);
847                     t->enabled = 0;
848                     t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
849                 }
850                 else {
851                     error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
852                           cg->chart_id, t->id, cg->id);
853                     debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
854                           cg->chart_id, t->id, cg->id);
855                     cg->enabled = 0;
856                     cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
857                 }
858
859                 break;
860             }
861         }
862     }
863
864     debug(D_CGROUP, "ADDED CGROUP: '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
865
866     return cg;
867 }
868
869 void cgroup_free(struct cgroup *cg) {
870     debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
871
872     freez(cg->cpuacct_usage.cpu_percpu);
873
874     freez(cg->cpuacct_stat.filename);
875     freez(cg->cpuacct_usage.filename);
876
877     freez(cg->memory.filename);
878     freez(cg->memory.filename_failcnt);
879     freez(cg->memory.filename_usage_in_bytes);
880     freez(cg->memory.filename_msw_usage_in_bytes);
881
882     freez(cg->io_service_bytes.filename);
883     freez(cg->io_serviced.filename);
884
885     freez(cg->throttle_io_service_bytes.filename);
886     freez(cg->throttle_io_serviced.filename);
887
888     freez(cg->io_merged.filename);
889     freez(cg->io_queued.filename);
890
891     freez(cg->id);
892     freez(cg->chart_id);
893     freez(cg->chart_title);
894
895     freez(cg);
896
897     cgroup_root_count--;
898 }
899
900 // find if a given cgroup exists
901 struct cgroup *cgroup_find(const char *id) {
902     debug(D_CGROUP, "searching for cgroup '%s'", id);
903
904     uint32_t hash = simple_hash(id);
905
906     struct cgroup *cg;
907     for(cg = cgroup_root; cg ; cg = cg->next) {
908         if(hash == cg->hash && strcmp(id, cg->id) == 0)
909             break;
910     }
911
912     debug(D_CGROUP, "cgroup '%s' %s in memory", id, (cg)?"found":"not found");
913     return cg;
914 }
915
916 // ----------------------------------------------------------------------------
917 // detect running cgroups
918
919 // callback for find_file_in_subdirs()
920 void found_subdir_in_dir(const char *dir) {
921     debug(D_CGROUP, "examining cgroup dir '%s'", dir);
922
923     struct cgroup *cg = cgroup_find(dir);
924     if(!cg) {
925         if(*dir && cgroup_max_depth > 0) {
926             int depth = 0;
927             const char *s;
928
929             for(s = dir; *s ;s++)
930                 if(unlikely(*s == '/'))
931                     depth++;
932
933             if(depth > cgroup_max_depth) {
934                 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
935                 return;
936             }
937         }
938         // debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
939         cg = cgroup_add(dir);
940     }
941
942     if(cg) cg->available = 1;
943 }
944
945 int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
946     if(!this) this = base;
947     debug(D_CGROUP, "searching for directories in '%s' (base '%s')", this?this:"", base);
948
949     size_t dirlen = strlen(this), baselen = strlen(base);
950
951     int ret = -1;
952     int enabled = -1;
953
954     const char *relative_path = &this[baselen];
955     if(!*relative_path) relative_path = "/";
956
957     DIR *dir = opendir(this);
958     if(!dir) {
959         error("Cannot read cgroups directory '%s'", base);
960         return ret;
961     }
962     ret = 1;
963
964     callback(relative_path);
965
966     struct dirent *de = NULL;
967     while((de = readdir(dir))) {
968         if(de->d_type == DT_DIR
969             && (
970                 (de->d_name[0] == '.' && de->d_name[1] == '\0')
971                 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
972                 ))
973             continue;
974
975         if(de->d_type == DT_DIR) {
976             if(enabled == -1) {
977                 const char *r = relative_path;
978                 if(*r == '\0') r = "/";
979
980                 // do not decent in directories we are not interested
981                 int def = 1;
982                 if(netdata_simple_pattern_list_matches(disabled_cgroup_paths, r))
983                     def = 0;
984
985                 // we check for this option here
986                 // so that the config will not have settings
987                 // for leaf directories
988                 char option[FILENAME_MAX + 1];
989                 snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
990                 option[FILENAME_MAX] = '\0';
991                 enabled = config_get_boolean("plugin:cgroups", option, def);
992             }
993
994             if(enabled) {
995                 char *s = mallocz(dirlen + strlen(de->d_name) + 2);
996                 strcpy(s, this);
997                 strcat(s, "/");
998                 strcat(s, de->d_name);
999                 int ret2 = find_dir_in_subdirs(base, s, callback);
1000                 if(ret2 > 0) ret += ret2;
1001                 freez(s);
1002             }
1003         }
1004     }
1005
1006     closedir(dir);
1007     return ret;
1008 }
1009
1010 void mark_all_cgroups_as_not_available() {
1011     debug(D_CGROUP, "marking all cgroups as not available");
1012
1013     struct cgroup *cg;
1014
1015     // mark all as not available
1016     for(cg = cgroup_root; cg ; cg = cg->next) {
1017         cg->available = 0;
1018     }
1019 }
1020
1021 void cleanup_all_cgroups() {
1022     struct cgroup *cg = cgroup_root, *last = NULL;
1023
1024     for(; cg ;) {
1025         if(!cg->available) {
1026             // enable the first duplicate cgroup
1027             {
1028                 struct cgroup *t;
1029                 for(t = cgroup_root; t ; t = t->next) {
1030                     if(t != cg && t->available && !t->enabled && t->options & CGROUP_OPTIONS_DISABLED_DUPLICATE && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
1031                         debug(D_CGROUP, "Enabling duplicate of cgroup '%s' with id '%s', because the original with id '%s' stopped.", t->chart_id, t->id, cg->id);
1032                         t->enabled = 1;
1033                         t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1034                         break;
1035                     }
1036                 }
1037             }
1038
1039             if(!last)
1040                 cgroup_root = cg->next;
1041             else
1042                 last->next = cg->next;
1043
1044             cgroup_free(cg);
1045
1046             if(!last)
1047                 cg = cgroup_root;
1048             else
1049                 cg = last->next;
1050         }
1051         else {
1052             last = cg;
1053             cg = cg->next;
1054         }
1055     }
1056 }
1057
1058 void find_all_cgroups() {
1059     debug(D_CGROUP, "searching for cgroups");
1060
1061     mark_all_cgroups_as_not_available();
1062
1063     if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
1064         if (find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
1065             cgroup_enable_cpuacct_stat = cgroup_enable_cpuacct_usage = 0;
1066             error("disabled cgroup cpu statistics.");
1067         }
1068     }
1069
1070     if(cgroup_enable_blkio) {
1071         if (find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
1072             cgroup_enable_blkio = 0;
1073             error("disabled cgroup blkio statistics.");
1074         }
1075     }
1076
1077     if(cgroup_enable_memory) {
1078         if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
1079             cgroup_enable_memory = 0;
1080             error("disabled cgroup memory statistics.");
1081         }
1082     }
1083
1084     if(cgroup_enable_devices) {
1085         if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
1086             cgroup_enable_devices = 0;
1087             error("disabled cgroup devices statistics.");
1088         }
1089     }
1090
1091     // remove any non-existing cgroups
1092     cleanup_all_cgroups();
1093
1094     struct cgroup *cg;
1095     struct stat buf;
1096     for(cg = cgroup_root; cg ; cg = cg->next) {
1097         // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
1098
1099         if(unlikely(!cg->available))
1100             continue;
1101
1102         debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
1103
1104         // check for newly added cgroups
1105         // and update the filenames they read
1106         char filename[FILENAME_MAX + 1];
1107         if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
1108             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
1109             if(stat(filename, &buf) != -1) {
1110                 cg->cpuacct_stat.filename = strdupz(filename);
1111                 debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
1112             }
1113             else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1114         }
1115
1116         if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename)) {
1117             snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
1118             if(stat(filename, &buf) != -1) {
1119                 cg->cpuacct_usage.filename = strdupz(filename);
1120                 debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
1121             }
1122             else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1123         }
1124
1125         if(unlikely(cgroup_enable_memory)) {
1126             if(unlikely(!cg->memory.filename)) {
1127                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
1128                 if(stat(filename, &buf) != -1) {
1129                     cg->memory.filename = strdupz(filename);
1130                     debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
1131                 }
1132                 else
1133                     debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1134             }
1135             if(unlikely(!cg->memory.filename_usage_in_bytes)) {
1136                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
1137                 if(stat(filename, &buf) != -1) {
1138                     cg->memory.filename_usage_in_bytes = strdupz(filename);
1139                     debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
1140                 }
1141                 else
1142                     debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1143             }
1144             if(unlikely(!cg->memory.filename_msw_usage_in_bytes)) {
1145                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
1146                 if(stat(filename, &buf) != -1) {
1147                     cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
1148                     debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
1149                 }
1150                 else
1151                     debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1152             }
1153             if(unlikely(!cg->memory.filename_failcnt)) {
1154                 snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
1155                 if(stat(filename, &buf) != -1) {
1156                     cg->memory.filename_failcnt = strdupz(filename);
1157                     debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
1158                 }
1159                 else
1160                     debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1161             }
1162         }
1163
1164         if(unlikely(cgroup_enable_blkio)) {
1165             if(unlikely(!cg->io_service_bytes.filename)) {
1166                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
1167                 if(stat(filename, &buf) != -1) {
1168                     cg->io_service_bytes.filename = strdupz(filename);
1169                     debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
1170                 }
1171                 else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1172             }
1173             if(unlikely(!cg->io_serviced.filename)) {
1174                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
1175                 if(stat(filename, &buf) != -1) {
1176                     cg->io_serviced.filename = strdupz(filename);
1177                     debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
1178                 }
1179                 else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1180             }
1181             if(unlikely(!cg->throttle_io_service_bytes.filename)) {
1182                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
1183                 if(stat(filename, &buf) != -1) {
1184                     cg->throttle_io_service_bytes.filename = strdupz(filename);
1185                     debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
1186                 }
1187                 else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1188             }
1189             if(unlikely(!cg->throttle_io_serviced.filename)) {
1190                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
1191                 if(stat(filename, &buf) != -1) {
1192                     cg->throttle_io_serviced.filename = strdupz(filename);
1193                     debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
1194                 }
1195                 else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1196             }
1197             if(unlikely(!cg->io_merged.filename)) {
1198                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
1199                 if(stat(filename, &buf) != -1) {
1200                     cg->io_merged.filename = strdupz(filename);
1201                     debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
1202                 }
1203                 else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1204             }
1205             if(unlikely(!cg->io_queued.filename)) {
1206                 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
1207                 if(stat(filename, &buf) != -1) {
1208                     cg->io_queued.filename = strdupz(filename);
1209                     debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
1210                 }
1211                 else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1212             }
1213         }
1214     }
1215
1216     debug(D_CGROUP, "done searching for cgroups");
1217     return;
1218 }
1219
1220 // ----------------------------------------------------------------------------
1221 // generate charts
1222
1223 #define CHART_TITLE_MAX 300
1224
1225 static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) {
1226     if(buffer[0]) return buffer;
1227
1228     if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0'))
1229         strncpy(buffer, "cgroup_root", len);
1230     else
1231         snprintfz(buffer, len, "cgroup_%s", id);
1232
1233     netdata_fix_chart_id(buffer);
1234     return buffer;
1235 }
1236
1237 void update_cgroup_charts(int update_every) {
1238     debug(D_CGROUP, "updating cgroups charts");
1239
1240     char type[RRD_ID_LENGTH_MAX + 1];
1241     char title[CHART_TITLE_MAX + 1];
1242
1243     struct cgroup *cg;
1244
1245     for(cg = cgroup_root; cg ; cg = cg->next) {
1246         if(!cg->available || !cg->enabled || cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)
1247             continue;
1248
1249         type[0] = '\0';
1250
1251         if(cg->cpuacct_stat.updated) {
1252             if(unlikely(!cg->st_cpu)) {
1253                 cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu");
1254                 if(!cg->st_cpu) {
1255                     snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
1256                     cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
1257
1258                     rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
1259                     rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
1260                 }
1261             }
1262             else rrdset_next(cg->st_cpu);
1263
1264             rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user);
1265             rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system);
1266             rrdset_done(cg->st_cpu);
1267         }
1268
1269         if(cg->cpuacct_usage.updated) {
1270             char id[RRD_ID_LENGTH_MAX + 1];
1271             unsigned int i;
1272
1273             if(unlikely(!cg->st_cpu_per_core)) {
1274                 cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core");
1275                 if(!cg->st_cpu_per_core) {
1276                     snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) Per Core for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
1277                     cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
1278
1279                     for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
1280                         snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1281                         rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
1282                     }
1283                 }
1284             }
1285             else rrdset_next(cg->st_cpu_per_core);
1286
1287             for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
1288                 snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1289                 rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]);
1290             }
1291             rrdset_done(cg->st_cpu_per_core);
1292         }
1293
1294         if(cg->memory.updated) {
1295             if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
1296                 if(unlikely(!cg->st_mem)) {
1297                     cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem");
1298                     if(!cg->st_mem) {
1299                         snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
1300                         cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every, RRDSET_TYPE_STACKED);
1301
1302                         rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1303                         rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1304                         if(cg->memory.has_dirty_swap)
1305                             rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1306                         rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1307                         rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1308                     }
1309                 }
1310                 else rrdset_next(cg->st_mem);
1311
1312                 rrddim_set(cg->st_mem, "cache", cg->memory.cache);
1313                 rrddim_set(cg->st_mem, "rss", cg->memory.rss);
1314                 if(cg->memory.has_dirty_swap)
1315                     rrddim_set(cg->st_mem, "swap", cg->memory.swap);
1316                 rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge);
1317                 rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file);
1318                 rrdset_done(cg->st_mem);
1319             }
1320
1321             if(unlikely(!cg->st_writeback)) {
1322                 cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback");
1323                 if(!cg->st_writeback) {
1324                     snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
1325                     cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300, update_every, RRDSET_TYPE_AREA);
1326
1327                     if(cg->memory.has_dirty_swap)
1328                         rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1329                     rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1330                 }
1331             }
1332             else rrdset_next(cg->st_writeback);
1333
1334             if(cg->memory.has_dirty_swap)
1335                 rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty);
1336             rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback);
1337             rrdset_done(cg->st_writeback);
1338
1339             if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
1340                 if(unlikely(!cg->st_mem_activity)) {
1341                     cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity");
1342                     if(!cg->st_mem_activity) {
1343                         snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
1344                         cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", 40400, update_every, RRDSET_TYPE_LINE);
1345
1346                         rrddim_add(cg->st_mem_activity, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1347                         rrddim_add(cg->st_mem_activity, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1348                     }
1349                 }
1350                 else rrdset_next(cg->st_mem_activity);
1351
1352                 rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.pgpgin);
1353                 rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.pgpgout);
1354                 rrdset_done(cg->st_mem_activity);
1355             }
1356
1357             if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
1358                 if(unlikely(!cg->st_pgfaults)) {
1359                     cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults");
1360                     if(!cg->st_pgfaults) {
1361                         snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
1362                         cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500, update_every, RRDSET_TYPE_LINE);
1363
1364                         rrddim_add(cg->st_pgfaults, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1365                         rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1366                     }
1367                 }
1368                 else rrdset_next(cg->st_pgfaults);
1369
1370                 rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.pgfault);
1371                 rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.pgmajfault);
1372                 rrdset_done(cg->st_pgfaults);
1373             }
1374         }
1375
1376         if(cg->memory.usage_in_bytes_updated) {
1377             if(unlikely(!cg->st_mem_usage)) {
1378                 cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage");
1379                 if(!cg->st_mem_usage) {
1380                     snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title);
1381                     cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200, update_every, RRDSET_TYPE_STACKED);
1382
1383                     rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1384                     rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1385                 }
1386             }
1387             else rrdset_next(cg->st_mem_usage);
1388
1389             rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes);
1390             rrddim_set(cg->st_mem_usage, "swap", (cg->memory.msw_usage_in_bytes > cg->memory.usage_in_bytes)?cg->memory.msw_usage_in_bytes - cg->memory.usage_in_bytes:0);
1391             rrdset_done(cg->st_mem_usage);
1392         }
1393
1394         if(cg->memory.failcnt_updated && cg->memory.failcnt > 0) {
1395             if(unlikely(!cg->st_mem_failcnt)) {
1396                 cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt");
1397                 if(!cg->st_mem_failcnt) {
1398                     snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
1399                     cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "MB", 40250, update_every, RRDSET_TYPE_LINE);
1400
1401                     rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
1402                 }
1403             }
1404             else rrdset_next(cg->st_mem_failcnt);
1405
1406             rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt);
1407             rrdset_done(cg->st_mem_failcnt);
1408         }
1409
1410         if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
1411             if(unlikely(!cg->st_io)) {
1412                 cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io");
1413                 if(!cg->st_io) {
1414                     snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1415                     cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1416
1417                     rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1418                     rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1419                 }
1420             }
1421             else rrdset_next(cg->st_io);
1422
1423             rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read);
1424             rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write);
1425             rrdset_done(cg->st_io);
1426         }
1427
1428         if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
1429             if(unlikely(!cg->st_serviced_ops)) {
1430                 cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops");
1431                 if(!cg->st_serviced_ops) {
1432                     snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s"
1433                               , cg->chart_title);
1434                     cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200, update_every, RRDSET_TYPE_LINE);
1435
1436                     rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1437                     rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1438                 }
1439             }
1440             else rrdset_next(cg->st_serviced_ops);
1441
1442             rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read);
1443             rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write);
1444             rrdset_done(cg->st_serviced_ops);
1445         }
1446
1447         if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
1448             if(unlikely(!cg->st_throttle_io)) {
1449                 cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io");
1450                 if(!cg->st_throttle_io) {
1451                     snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1452                     cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1453
1454                     rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1455                     rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1456                 }
1457             }
1458             else rrdset_next(cg->st_throttle_io);
1459
1460             rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read);
1461             rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write);
1462             rrdset_done(cg->st_throttle_io);
1463         }
1464
1465
1466         if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
1467             if(unlikely(!cg->st_throttle_serviced_ops)) {
1468                 cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops");
1469                 if(!cg->st_throttle_serviced_ops) {
1470                     snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
1471                     cg->st_throttle_serviced_ops = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200, update_every, RRDSET_TYPE_LINE);
1472
1473                     rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1474                     rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1475                 }
1476             }
1477             else rrdset_next(cg->st_throttle_serviced_ops);
1478
1479             rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read);
1480             rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write);
1481             rrdset_done(cg->st_throttle_serviced_ops);
1482         }
1483
1484         if(cg->io_queued.updated) {
1485             if(unlikely(!cg->st_queued_ops)) {
1486                 cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops");
1487                 if(!cg->st_queued_ops) {
1488                     snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
1489                     cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000, update_every, RRDSET_TYPE_LINE);
1490
1491                     rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
1492                     rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
1493                 }
1494             }
1495             else rrdset_next(cg->st_queued_ops);
1496
1497             rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read);
1498             rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write);
1499             rrdset_done(cg->st_queued_ops);
1500         }
1501
1502         if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
1503             if(unlikely(!cg->st_merged_ops)) {
1504                 cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops");
1505                 if(!cg->st_merged_ops) {
1506                     snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
1507                     cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100, update_every, RRDSET_TYPE_LINE);
1508
1509                     rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1510                     rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1511                 }
1512             }
1513             else rrdset_next(cg->st_merged_ops);
1514
1515             rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read);
1516             rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write);
1517             rrdset_done(cg->st_merged_ops);
1518         }
1519     }
1520
1521     debug(D_CGROUP, "done updating cgroups charts");
1522 }
1523
1524 // ----------------------------------------------------------------------------
1525 // cgroups main
1526
1527 void *cgroups_main(void *ptr) {
1528     struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
1529
1530     info("CGROUP Plugin thread created with task id %d", gettid());
1531
1532     if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
1533         error("Cannot set pthread cancel type to DEFERRED.");
1534
1535     if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
1536         error("Cannot set pthread cancel state to ENABLE.");
1537
1538     struct rusage thread;
1539
1540     // when ZERO, attempt to do it
1541     int vdo_cpu_netdata = config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
1542
1543     read_cgroup_plugin_configuration();
1544
1545     RRDSET *stcpu_thread = NULL;
1546
1547     usec_t step = cgroup_update_every * USEC_PER_SEC;
1548     usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_next = 0;
1549     for(;;) {
1550         usec_t now = now_monotonic_usec();
1551         usec_t next = now - (now % step) + step;
1552
1553         while(now < next) {
1554             sleep_usec(next - now);
1555             now = now_monotonic_usec();
1556         }
1557
1558         if(unlikely(netdata_exit)) break;
1559
1560         // BEGIN -- the job to be done
1561
1562         if(unlikely(now >= find_next)) {
1563             find_all_cgroups();
1564             find_next = now + find_every;
1565         }
1566
1567         read_all_cgroups(cgroup_root);
1568         update_cgroup_charts(cgroup_update_every);
1569
1570         // END -- the job is done
1571
1572         // --------------------------------------------------------------------
1573
1574         if(vdo_cpu_netdata) {
1575             getrusage(RUSAGE_THREAD, &thread);
1576
1577             if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
1578             if(!stcpu_thread) {
1579                 stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, cgroup_update_every, RRDSET_TYPE_STACKED);
1580
1581                 rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
1582                 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
1583             }
1584             else rrdset_next(stcpu_thread);
1585
1586             rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
1587             rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
1588             rrdset_done(stcpu_thread);
1589         }
1590     }
1591
1592     info("CGROUP thread exiting");
1593
1594     static_thread->enabled = 0;
1595     pthread_exit(NULL);
1596     return NULL;
1597 }