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