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