3 // ----------------------------------------------------------------------------
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_new_cgroups_detected_at_runtime = 1;
12 static int cgroup_check_for_new_every = 10;
13 static char *cgroup_cpuacct_base = NULL;
14 static char *cgroup_blkio_base = NULL;
15 static char *cgroup_memory_base = NULL;
16 static char *cgroup_devices_base = NULL;
18 static int cgroup_root_count = 0;
19 static int cgroup_root_max = 500;
20 static int cgroup_max_depth = 0;
22 static NETDATA_SIMPLE_PATTERN *disabled_cgroups_patterns = NULL;
23 static NETDATA_SIMPLE_PATTERN *disabled_cgroup_paths = NULL;
24 static NETDATA_SIMPLE_PATTERN *disabled_cgroup_renames = NULL;
25 static NETDATA_SIMPLE_PATTERN *systemd_services_cgroups = NULL;
27 static char *cgroups_rename_script = PLUGINS_DIR "/cgroup-name.sh";
29 void read_cgroup_plugin_configuration() {
30 cgroup_check_for_new_every = (int)config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
32 cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
33 cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
34 cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
35 cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
37 char filename[FILENAME_MAX + 1], *s;
38 struct mountinfo *mi, *root = mountinfo_read(0);
40 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
41 if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
43 error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
44 s = "/sys/fs/cgroup/cpuacct";
46 else s = mi->mount_point;
47 snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
48 cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
50 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
51 if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
53 error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
54 s = "/sys/fs/cgroup/blkio";
56 else s = mi->mount_point;
57 snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
58 cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
60 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
61 if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
63 error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
64 s = "/sys/fs/cgroup/memory";
66 else s = mi->mount_point;
67 snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
68 cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
70 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
71 if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
73 error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
74 s = "/sys/fs/cgroup/devices";
76 else s = mi->mount_point;
77 snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
78 cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
80 cgroup_root_max = (int)config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
81 cgroup_max_depth = (int)config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
83 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);
85 disabled_cgroups_patterns = netdata_simple_pattern_list_create(
86 config_get("plugin:cgroups", "disable by default cgroups matching",
87 " / /lxc /docker /libvirt /qemu /systemd "
88 " /system /system.slice "
89 " /machine /machine.slice "
97 " */ns " // /lxc/*/ns #1397
98 ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
100 disabled_cgroup_paths = netdata_simple_pattern_list_create(
101 config_get("plugin:cgroups", "do not search for cgroups in paths matching",
108 ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
110 cgroups_rename_script = config_get("plugin:cgroups", "script to get cgroup names", cgroups_rename_script);
112 disabled_cgroup_renames = netdata_simple_pattern_list_create(
113 config_get("plugin:cgroups", "do not run script to rename cgroups matching",
122 ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
124 systemd_services_cgroups = netdata_simple_pattern_list_create(
125 config_get("plugin:cgroups", "cgroups to match as systemd services",
127 ), NETDATA_SIMPLE_PATTERN_MODE_EXACT);
129 mountinfo_free(root);
132 // ----------------------------------------------------------------------------
140 unsigned long long Read;
141 unsigned long long Write;
143 unsigned long long Sync;
144 unsigned long long Async;
145 unsigned long long Total;
149 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
157 unsigned long long cache;
158 unsigned long long rss;
159 unsigned long long rss_huge;
160 unsigned long long mapped_file;
161 unsigned long long writeback;
162 unsigned long long dirty;
163 unsigned long long swap;
164 unsigned long long pgpgin;
165 unsigned long long pgpgout;
166 unsigned long long pgfault;
167 unsigned long long pgmajfault;
169 unsigned long long inactive_anon;
170 unsigned long long active_anon;
171 unsigned long long inactive_file;
172 unsigned long long active_file;
173 unsigned long long unevictable;
174 unsigned long long hierarchical_memory_limit;
175 unsigned long long total_cache;
176 unsigned long long total_rss;
177 unsigned long long total_rss_huge;
178 unsigned long long total_mapped_file;
179 unsigned long long total_writeback;
180 unsigned long long total_dirty;
181 unsigned long long total_swap;
182 unsigned long long total_pgpgin;
183 unsigned long long total_pgpgout;
184 unsigned long long total_pgfault;
185 unsigned long long total_pgmajfault;
186 unsigned long long total_inactive_anon;
187 unsigned long long total_active_anon;
188 unsigned long long total_inactive_file;
189 unsigned long long total_active_file;
190 unsigned long long total_unevictable;
193 int usage_in_bytes_updated;
194 char *filename_usage_in_bytes;
195 unsigned long long usage_in_bytes;
197 int msw_usage_in_bytes_updated;
198 char *filename_msw_usage_in_bytes;
199 unsigned long long msw_usage_in_bytes;
202 char *filename_failcnt;
203 unsigned long long failcnt;
206 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
207 struct cpuacct_stat {
212 unsigned long long user;
213 unsigned long long system;
216 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
217 struct cpuacct_usage {
223 unsigned long long *cpu_percpu;
226 #define CGROUP_OPTIONS_DISABLED_DUPLICATE 0x00000001
227 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
232 char available; // found in the filesystem
233 char enabled; // enabled in the config
243 struct cpuacct_stat cpuacct_stat;
244 struct cpuacct_usage cpuacct_usage;
246 struct memory memory;
248 struct blkio io_service_bytes; // bytes
249 struct blkio io_serviced; // operations
251 struct blkio throttle_io_service_bytes; // bytes
252 struct blkio throttle_io_serviced; // operations
254 struct blkio io_merged; // operations
255 struct blkio io_queued; // operations
258 RRDSET *st_cpu_per_core;
260 RRDSET *st_writeback;
261 RRDSET *st_mem_activity;
263 RRDSET *st_mem_usage;
264 RRDSET *st_mem_failcnt;
266 RRDSET *st_serviced_ops;
267 RRDSET *st_throttle_io;
268 RRDSET *st_throttle_serviced_ops;
269 RRDSET *st_queued_ops;
270 RRDSET *st_merged_ops;
274 } *cgroup_root = NULL;
276 // ----------------------------------------------------------------------------
277 // read values from /sys
279 void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
280 static procfile *ff = NULL;
282 static uint32_t user_hash = 0;
283 static uint32_t system_hash = 0;
285 if(unlikely(user_hash == 0)) {
286 user_hash = simple_hash("user");
287 system_hash = simple_hash("system");
292 ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
295 ff = procfile_readall(ff);
298 unsigned long i, lines = procfile_lines(ff);
301 error("File '%s' should have 1+ lines.", cp->filename);
305 for(i = 0; i < lines ; i++) {
306 char *s = procfile_lineword(ff, i, 0);
307 uint32_t hash = simple_hash(s);
309 if(hash == user_hash && !strcmp(s, "user"))
310 cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
312 else if(hash == system_hash && !strcmp(s, "system"))
313 cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
318 // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
322 void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
323 static procfile *ff = NULL;
327 ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
330 ff = procfile_readall(ff);
333 if(procfile_lines(ff) < 1) {
334 error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
338 unsigned long i = procfile_linewords(ff, 0);
341 // we may have 1 more CPU reported
343 char *s = procfile_lineword(ff, 0, i - 1);
349 freez(ca->cpu_percpu);
350 ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
351 ca->cpus = (unsigned int)i;
354 for(i = 0; i < ca->cpus ;i++) {
355 ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
356 // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
363 void cgroup_read_blkio(struct blkio *io) {
364 static procfile *ff = NULL;
366 static uint32_t Read_hash = 0;
367 static uint32_t Write_hash = 0;
369 static uint32_t Sync_hash = 0;
370 static uint32_t Async_hash = 0;
371 static uint32_t Total_hash = 0;
374 if(unlikely(Read_hash == 0)) {
375 Read_hash = simple_hash("Read");
376 Write_hash = simple_hash("Write");
378 Sync_hash = simple_hash("Sync");
379 Async_hash = simple_hash("Async");
380 Total_hash = simple_hash("Total");
386 ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
389 ff = procfile_readall(ff);
392 unsigned long i, lines = procfile_lines(ff);
395 error("File '%s' should have 1+ lines.", io->filename);
407 for(i = 0; i < lines ; i++) {
408 char *s = procfile_lineword(ff, i, 1);
409 uint32_t hash = simple_hash(s);
411 if(hash == Read_hash && !strcmp(s, "Read"))
412 io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
414 else if(hash == Write_hash && !strcmp(s, "Write"))
415 io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
418 else if(hash == Sync_hash && !strcmp(s, "Sync"))
419 io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
421 else if(hash == Async_hash && !strcmp(s, "Async"))
422 io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
424 else if(hash == Total_hash && !strcmp(s, "Total"))
425 io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
430 // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
434 void cgroup_read_memory(struct memory *mem) {
435 static procfile *ff = NULL;
437 static uint32_t cache_hash = 0;
438 static uint32_t rss_hash = 0;
439 static uint32_t rss_huge_hash = 0;
440 static uint32_t mapped_file_hash = 0;
441 static uint32_t writeback_hash = 0;
442 static uint32_t dirty_hash = 0;
443 static uint32_t swap_hash = 0;
444 static uint32_t pgpgin_hash = 0;
445 static uint32_t pgpgout_hash = 0;
446 static uint32_t pgfault_hash = 0;
447 static uint32_t pgmajfault_hash = 0;
449 static uint32_t inactive_anon_hash = 0;
450 static uint32_t active_anon_hash = 0;
451 static uint32_t inactive_file_hash = 0;
452 static uint32_t active_file_hash = 0;
453 static uint32_t unevictable_hash = 0;
454 static uint32_t hierarchical_memory_limit_hash = 0;
455 static uint32_t total_cache_hash = 0;
456 static uint32_t total_rss_hash = 0;
457 static uint32_t total_rss_huge_hash = 0;
458 static uint32_t total_mapped_file_hash = 0;
459 static uint32_t total_writeback_hash = 0;
460 static uint32_t total_dirty_hash = 0;
461 static uint32_t total_swap_hash = 0;
462 static uint32_t total_pgpgin_hash = 0;
463 static uint32_t total_pgpgout_hash = 0;
464 static uint32_t total_pgfault_hash = 0;
465 static uint32_t total_pgmajfault_hash = 0;
466 static uint32_t total_inactive_anon_hash = 0;
467 static uint32_t total_active_anon_hash = 0;
468 static uint32_t total_inactive_file_hash = 0;
469 static uint32_t total_active_file_hash = 0;
470 static uint32_t total_unevictable_hash = 0;
472 if(unlikely(cache_hash == 0)) {
473 cache_hash = simple_hash("cache");
474 rss_hash = simple_hash("rss");
475 rss_huge_hash = simple_hash("rss_huge");
476 mapped_file_hash = simple_hash("mapped_file");
477 writeback_hash = simple_hash("writeback");
478 dirty_hash = simple_hash("dirty");
479 swap_hash = simple_hash("swap");
480 pgpgin_hash = simple_hash("pgpgin");
481 pgpgout_hash = simple_hash("pgpgout");
482 pgfault_hash = simple_hash("pgfault");
483 pgmajfault_hash = simple_hash("pgmajfault");
485 inactive_anon_hash = simple_hash("inactive_anon");
486 active_anon_hash = simple_hash("active_anon");
487 inactive_file_hash = simple_hash("inactive_file");
488 active_file_hash = simple_hash("active_file");
489 unevictable_hash = simple_hash("unevictable");
490 hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
491 total_cache_hash = simple_hash("total_cache");
492 total_rss_hash = simple_hash("total_rss");
493 total_rss_huge_hash = simple_hash("total_rss_huge");
494 total_mapped_file_hash = simple_hash("total_mapped_file");
495 total_writeback_hash = simple_hash("total_writeback");
496 total_dirty_hash = simple_hash("total_dirty");
497 total_swap_hash = simple_hash("total_swap");
498 total_pgpgin_hash = simple_hash("total_pgpgin");
499 total_pgpgout_hash = simple_hash("total_pgpgout");
500 total_pgfault_hash = simple_hash("total_pgfault");
501 total_pgmajfault_hash = simple_hash("total_pgmajfault");
502 total_inactive_anon_hash = simple_hash("total_inactive_anon");
503 total_active_anon_hash = simple_hash("total_active_anon");
504 total_inactive_file_hash = simple_hash("total_inactive_file");
505 total_active_file_hash = simple_hash("total_active_file");
506 total_unevictable_hash = simple_hash("total_unevictable");
512 ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
515 ff = procfile_readall(ff);
518 unsigned long i, lines = procfile_lines(ff);
521 error("File '%s' should have 1+ lines.", mem->filename);
525 for(i = 0; i < lines ; i++) {
526 char *s = procfile_lineword(ff, i, 0);
527 uint32_t hash = simple_hash(s);
529 if(hash == cache_hash && !strcmp(s, "cache"))
530 mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
532 else if(hash == rss_hash && !strcmp(s, "rss"))
533 mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
535 else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
536 mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
538 else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
539 mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
541 else if(hash == writeback_hash && !strcmp(s, "writeback"))
542 mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
544 else if(hash == dirty_hash && !strcmp(s, "dirty")) {
545 mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
546 mem->has_dirty_swap = 1;
549 else if(hash == swap_hash && !strcmp(s, "swap")) {
550 mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
551 mem->has_dirty_swap = 1;
554 else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
555 mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
557 else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
558 mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
560 else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
561 mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
563 else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
564 mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
567 else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
568 mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
570 else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
571 mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
573 else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
574 mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
576 else if(hash == active_file_hash && !strcmp(s, "active_file"))
577 mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
579 else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
580 mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
582 else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
583 mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
585 else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
586 mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
588 else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
589 mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
591 else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
592 mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
594 else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
595 mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
597 else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
598 mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
600 else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
601 mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
603 else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
604 mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
606 else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
607 mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
609 else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
610 mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
612 else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
613 mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
615 else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
616 mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
618 else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
619 mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
621 else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
622 mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
624 else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
625 mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
627 else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
628 mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
630 else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
631 mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
635 // 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);
640 mem->usage_in_bytes_updated = 0;
641 if(mem->filename_usage_in_bytes) {
642 if(likely(!read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes)))
643 mem->usage_in_bytes_updated = 1;
646 mem->msw_usage_in_bytes_updated = 0;
647 if(mem->filename_msw_usage_in_bytes) {
648 if(likely(!read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes)))
649 mem->msw_usage_in_bytes_updated = 1;
652 mem->failcnt_updated = 0;
653 if(mem->filename_failcnt) {
654 if(likely(!read_single_number_file(mem->filename_failcnt, &mem->failcnt)))
655 mem->failcnt_updated = 1;
659 void cgroup_read(struct cgroup *cg) {
660 debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
662 cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
663 cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
664 cgroup_read_memory(&cg->memory);
665 cgroup_read_blkio(&cg->io_service_bytes);
666 cgroup_read_blkio(&cg->io_serviced);
667 cgroup_read_blkio(&cg->throttle_io_service_bytes);
668 cgroup_read_blkio(&cg->throttle_io_serviced);
669 cgroup_read_blkio(&cg->io_merged);
670 cgroup_read_blkio(&cg->io_queued);
673 void read_all_cgroups(struct cgroup *root) {
674 debug(D_CGROUP, "reading metrics for all cgroups");
678 for(cg = root; cg ; cg = cg->next)
679 if(cg->enabled && cg->available)
683 // ----------------------------------------------------------------------------
684 // add/remove/find cgroup objects
686 #define CGROUP_CHARTID_LINE_MAX 1024
688 static char *cgroup_title_strdupz(const char *s) {
689 if(!s || !*s) s = "/";
691 if(*s == '/' && s[1] != '\0') s++;
693 char *r = strdupz(s);
694 netdata_fix_chart_name(r);
699 static char *cgroup_chart_id_strdupz(const char *s) {
700 if(!s || !*s) s = "/";
702 if(*s == '/' && s[1] != '\0') s++;
704 char *r = strdupz(s);
705 netdata_fix_chart_id(r);
710 void cgroup_get_chart_name(struct cgroup *cg) {
711 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);
713 if(!netdata_simple_pattern_list_matches(disabled_cgroup_renames, cg->id) &&
714 !netdata_simple_pattern_list_matches(disabled_cgroup_renames, cg->chart_id)) {
717 char buffer[CGROUP_CHARTID_LINE_MAX + 1];
719 snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'", cgroups_rename_script, cg->chart_id);
721 debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
722 FILE *fp = mypopen(buffer, &cgroup_pid);
724 // debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
725 char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
726 // debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
727 mypclose(fp, cgroup_pid);
728 // debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
730 if(s && *s && *s != '\n') {
731 debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
735 freez(cg->chart_title);
736 cg->chart_title = cgroup_title_strdupz(s);
739 cg->chart_id = cgroup_chart_id_strdupz(s);
740 cg->hash_chart = simple_hash(cg->chart_id);
744 error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
746 debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
749 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);
752 if(netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->id) ||
753 netdata_simple_pattern_list_matches(systemd_services_cgroups, cg->chart_id)) {
754 debug(D_CGROUP, "cgroup '%s' with chart id '%s' (title: '%s') matches systemd services cgroups", cg->id, cg->chart_id, cg->chart_title);
756 char buffer[CGROUP_CHARTID_LINE_MAX + 1];
757 cg->options |= CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE;
759 strncpy(buffer, cg->id, CGROUP_CHARTID_LINE_MAX);
762 // skip to the last slash
763 size_t len = strlen(s);
764 while(len--) if(unlikely(s[len] == '/')) break;
765 if(len) s = &s[len+1];
769 //while(len--) if(unlikely(s[len] == '.')) break;
770 //if(len) s[len] = '\0';
772 freez(cg->chart_title);
773 cg->chart_title = cgroup_title_strdupz(s);
776 cg->chart_id = cgroup_chart_id_strdupz(s);
777 cg->hash_chart = simple_hash(cg->chart_id);
779 debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
782 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);
785 struct cgroup *cgroup_add(const char *id) {
786 if(!id || !*id) id = "/";
787 debug(D_CGROUP, "adding to list, cgroup with id '%s'", id);
789 if(cgroup_root_count >= cgroup_root_max) {
790 info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
794 int def = netdata_simple_pattern_list_matches(disabled_cgroups_patterns, id)?0:cgroup_enable_new_cgroups_detected_at_runtime;
795 struct cgroup *cg = callocz(1, sizeof(struct cgroup));
797 cg->id = strdupz(id);
798 cg->hash = simple_hash(cg->id);
800 cg->chart_title = cgroup_title_strdupz(id);
802 cg->chart_id = cgroup_chart_id_strdupz(id);
803 cg->hash_chart = simple_hash(cg->chart_id);
810 for(e = cgroup_root; e->next ;e = e->next) ;
816 // fix the name by calling the external script
817 cgroup_get_chart_name(cg);
819 char option[FILENAME_MAX + 1];
820 snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
821 cg->enabled = (char)config_get_boolean("plugin:cgroups", option, def);
825 for (t = cgroup_root; t; t = t->next) {
826 if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
827 if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
828 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'.",
829 cg->chart_id, t->id, cg->id, t->id);
830 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'.",
831 cg->chart_id, t->id, cg->id, t->id);
833 t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
836 error("Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
837 cg->chart_id, t->id, cg->id);
838 debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
839 cg->chart_id, t->id, cg->id);
841 cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
849 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");
854 void cgroup_free(struct cgroup *cg) {
855 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");
857 freez(cg->cpuacct_usage.cpu_percpu);
859 freez(cg->cpuacct_stat.filename);
860 freez(cg->cpuacct_usage.filename);
862 freez(cg->memory.filename);
863 freez(cg->memory.filename_failcnt);
864 freez(cg->memory.filename_usage_in_bytes);
865 freez(cg->memory.filename_msw_usage_in_bytes);
867 freez(cg->io_service_bytes.filename);
868 freez(cg->io_serviced.filename);
870 freez(cg->throttle_io_service_bytes.filename);
871 freez(cg->throttle_io_serviced.filename);
873 freez(cg->io_merged.filename);
874 freez(cg->io_queued.filename);
878 freez(cg->chart_title);
885 // find if a given cgroup exists
886 struct cgroup *cgroup_find(const char *id) {
887 debug(D_CGROUP, "searching for cgroup '%s'", id);
889 uint32_t hash = simple_hash(id);
892 for(cg = cgroup_root; cg ; cg = cg->next) {
893 if(hash == cg->hash && strcmp(id, cg->id) == 0)
897 debug(D_CGROUP, "cgroup '%s' %s in memory", id, (cg)?"found":"not found");
901 // ----------------------------------------------------------------------------
902 // detect running cgroups
904 // callback for find_file_in_subdirs()
905 void found_subdir_in_dir(const char *dir) {
906 debug(D_CGROUP, "examining cgroup dir '%s'", dir);
908 struct cgroup *cg = cgroup_find(dir);
910 if(*dir && cgroup_max_depth > 0) {
914 for(s = dir; *s ;s++)
915 if(unlikely(*s == '/'))
918 if(depth > cgroup_max_depth) {
919 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
923 // debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
924 cg = cgroup_add(dir);
927 if(cg) cg->available = 1;
930 int find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
931 if(!this) this = base;
932 debug(D_CGROUP, "searching for directories in '%s' (base '%s')", this?this:"", base);
934 size_t dirlen = strlen(this), baselen = strlen(base);
939 const char *relative_path = &this[baselen];
940 if(!*relative_path) relative_path = "/";
942 DIR *dir = opendir(this);
944 error("Cannot read cgroups directory '%s'", base);
949 callback(relative_path);
951 struct dirent *de = NULL;
952 while((de = readdir(dir))) {
953 if(de->d_type == DT_DIR
955 (de->d_name[0] == '.' && de->d_name[1] == '\0')
956 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
960 if(de->d_type == DT_DIR) {
962 const char *r = relative_path;
963 if(*r == '\0') r = "/";
965 // do not decent in directories we are not interested
967 if(netdata_simple_pattern_list_matches(disabled_cgroup_paths, r))
970 // we check for this option here
971 // so that the config will not have settings
972 // for leaf directories
973 char option[FILENAME_MAX + 1];
974 snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
975 option[FILENAME_MAX] = '\0';
976 enabled = config_get_boolean("plugin:cgroups", option, def);
980 char *s = mallocz(dirlen + strlen(de->d_name) + 2);
983 strcat(s, de->d_name);
984 int ret2 = find_dir_in_subdirs(base, s, callback);
985 if(ret2 > 0) ret += ret2;
995 void mark_all_cgroups_as_not_available() {
996 debug(D_CGROUP, "marking all cgroups as not available");
1000 // mark all as not available
1001 for(cg = cgroup_root; cg ; cg = cg->next) {
1006 void cleanup_all_cgroups() {
1007 struct cgroup *cg = cgroup_root, *last = NULL;
1010 if(!cg->available) {
1011 // enable the first duplicate cgroup
1014 for(t = cgroup_root; t ; t = t->next) {
1015 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)) {
1016 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);
1018 t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1025 cgroup_root = cg->next;
1027 last->next = cg->next;
1043 void find_all_cgroups() {
1044 debug(D_CGROUP, "searching for cgroups");
1046 mark_all_cgroups_as_not_available();
1048 if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
1049 if (find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
1050 cgroup_enable_cpuacct_stat = cgroup_enable_cpuacct_usage = 0;
1051 error("disabled cgroup cpu statistics.");
1055 if(cgroup_enable_blkio) {
1056 if (find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
1057 cgroup_enable_blkio = 0;
1058 error("disabled cgroup blkio statistics.");
1062 if(cgroup_enable_memory) {
1063 if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
1064 cgroup_enable_memory = 0;
1065 error("disabled cgroup memory statistics.");
1069 if(cgroup_enable_devices) {
1070 if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
1071 cgroup_enable_devices = 0;
1072 error("disabled cgroup devices statistics.");
1076 // remove any non-existing cgroups
1077 cleanup_all_cgroups();
1081 for(cg = cgroup_root; cg ; cg = cg->next) {
1082 // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
1084 if(unlikely(!cg->available))
1087 debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
1089 // check for newly added cgroups
1090 // and update the filenames they read
1091 char filename[FILENAME_MAX + 1];
1092 if(unlikely(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename)) {
1093 snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
1094 if(stat(filename, &buf) != -1) {
1095 cg->cpuacct_stat.filename = strdupz(filename);
1096 debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
1098 else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1101 if(unlikely(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename)) {
1102 snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
1103 if(stat(filename, &buf) != -1) {
1104 cg->cpuacct_usage.filename = strdupz(filename);
1105 debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
1107 else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1110 if(unlikely(cgroup_enable_memory)) {
1111 if(unlikely(!cg->memory.filename)) {
1112 snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
1113 if(stat(filename, &buf) != -1) {
1114 cg->memory.filename = strdupz(filename);
1115 debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
1118 debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1120 if(unlikely(!cg->memory.filename_usage_in_bytes)) {
1121 snprintfz(filename, FILENAME_MAX, "%s%s/memory.usage_in_bytes", cgroup_memory_base, cg->id);
1122 if(stat(filename, &buf) != -1) {
1123 cg->memory.filename_usage_in_bytes = strdupz(filename);
1124 debug(D_CGROUP, "memory.usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_usage_in_bytes);
1127 debug(D_CGROUP, "memory.usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1129 if(unlikely(!cg->memory.filename_msw_usage_in_bytes)) {
1130 snprintfz(filename, FILENAME_MAX, "%s%s/memory.msw_usage_in_bytes", cgroup_memory_base, cg->id);
1131 if(stat(filename, &buf) != -1) {
1132 cg->memory.filename_msw_usage_in_bytes = strdupz(filename);
1133 debug(D_CGROUP, "memory.msw_usage_in_bytes filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_msw_usage_in_bytes);
1136 debug(D_CGROUP, "memory.msw_usage_in_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1138 if(unlikely(!cg->memory.filename_failcnt)) {
1139 snprintfz(filename, FILENAME_MAX, "%s%s/memory.failcnt", cgroup_memory_base, cg->id);
1140 if(stat(filename, &buf) != -1) {
1141 cg->memory.filename_failcnt = strdupz(filename);
1142 debug(D_CGROUP, "memory.failcnt filename for cgroup '%s': '%s'", cg->id, cg->memory.filename_failcnt);
1145 debug(D_CGROUP, "memory.failcnt file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1149 if(unlikely(cgroup_enable_blkio)) {
1150 if(unlikely(!cg->io_service_bytes.filename)) {
1151 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
1152 if(stat(filename, &buf) != -1) {
1153 cg->io_service_bytes.filename = strdupz(filename);
1154 debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
1156 else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1158 if(unlikely(!cg->io_serviced.filename)) {
1159 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
1160 if(stat(filename, &buf) != -1) {
1161 cg->io_serviced.filename = strdupz(filename);
1162 debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
1164 else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1166 if(unlikely(!cg->throttle_io_service_bytes.filename)) {
1167 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
1168 if(stat(filename, &buf) != -1) {
1169 cg->throttle_io_service_bytes.filename = strdupz(filename);
1170 debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
1172 else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1174 if(unlikely(!cg->throttle_io_serviced.filename)) {
1175 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
1176 if(stat(filename, &buf) != -1) {
1177 cg->throttle_io_serviced.filename = strdupz(filename);
1178 debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
1180 else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1182 if(unlikely(!cg->io_merged.filename)) {
1183 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
1184 if(stat(filename, &buf) != -1) {
1185 cg->io_merged.filename = strdupz(filename);
1186 debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
1188 else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1190 if(unlikely(!cg->io_queued.filename)) {
1191 snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
1192 if(stat(filename, &buf) != -1) {
1193 cg->io_queued.filename = strdupz(filename);
1194 debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
1196 else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
1201 debug(D_CGROUP, "done searching for cgroups");
1205 // ----------------------------------------------------------------------------
1208 #define CHART_TITLE_MAX 300
1210 static inline char *cgroup_chart_type(char *buffer, const char *id, size_t len) {
1211 if(buffer[0]) return buffer;
1213 if(id[0] == '\0' || (id[0] == '/' && id[1] == '\0'))
1214 strncpy(buffer, "cgroup_root", len);
1216 snprintfz(buffer, len, "cgroup_%s", id);
1218 netdata_fix_chart_id(buffer);
1222 void update_cgroup_charts(int update_every) {
1223 debug(D_CGROUP, "updating cgroups charts");
1225 char type[RRD_ID_LENGTH_MAX + 1];
1226 char title[CHART_TITLE_MAX + 1];
1230 for(cg = cgroup_root; cg ; cg = cg->next) {
1231 if(!cg->available || !cg->enabled || cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)
1236 if(cg->cpuacct_stat.updated) {
1237 if(unlikely(!cg->st_cpu)) {
1238 cg->st_cpu = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu");
1240 snprintfz(title, CHART_TITLE_MAX, "CPU Usage (%d%% = %d core%s) for cgroup %s", (processors * 100), processors, (processors > 1) ? "s" : "", cg->chart_title);
1241 cg->st_cpu = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
1243 rrddim_add(cg->st_cpu, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
1244 rrddim_add(cg->st_cpu, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
1247 else rrdset_next(cg->st_cpu);
1249 rrddim_set(cg->st_cpu, "user", cg->cpuacct_stat.user);
1250 rrddim_set(cg->st_cpu, "system", cg->cpuacct_stat.system);
1251 rrdset_done(cg->st_cpu);
1254 if(cg->cpuacct_usage.updated) {
1255 char id[RRD_ID_LENGTH_MAX + 1];
1258 if(unlikely(!cg->st_cpu_per_core)) {
1259 cg->st_cpu_per_core = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "cpu_per_core");
1260 if(!cg->st_cpu_per_core) {
1261 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);
1262 cg->st_cpu_per_core = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
1264 for(i = 0; i < cg->cpuacct_usage.cpus; i++) {
1265 snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1266 rrddim_add(cg->st_cpu_per_core, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
1270 else rrdset_next(cg->st_cpu_per_core);
1272 for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
1273 snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
1274 rrddim_set(cg->st_cpu_per_core, id, cg->cpuacct_usage.cpu_percpu[i]);
1276 rrdset_done(cg->st_cpu_per_core);
1279 if(cg->memory.updated) {
1280 if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
1281 if(unlikely(!cg->st_mem)) {
1282 cg->st_mem = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem");
1284 snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
1285 cg->st_mem = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40210, update_every, RRDSET_TYPE_STACKED);
1287 rrddim_add(cg->st_mem, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1288 rrddim_add(cg->st_mem, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1289 if(cg->memory.has_dirty_swap)
1290 rrddim_add(cg->st_mem, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1291 rrddim_add(cg->st_mem, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1292 rrddim_add(cg->st_mem, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1295 else rrdset_next(cg->st_mem);
1297 rrddim_set(cg->st_mem, "cache", cg->memory.cache);
1298 rrddim_set(cg->st_mem, "rss", cg->memory.rss);
1299 if(cg->memory.has_dirty_swap)
1300 rrddim_set(cg->st_mem, "swap", cg->memory.swap);
1301 rrddim_set(cg->st_mem, "rss_huge", cg->memory.rss_huge);
1302 rrddim_set(cg->st_mem, "mapped_file", cg->memory.mapped_file);
1303 rrdset_done(cg->st_mem);
1306 if(unlikely(!cg->st_writeback)) {
1307 cg->st_writeback = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "writeback");
1308 if(!cg->st_writeback) {
1309 snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
1310 cg->st_writeback = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300, update_every, RRDSET_TYPE_AREA);
1312 if(cg->memory.has_dirty_swap)
1313 rrddim_add(cg->st_writeback, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1314 rrddim_add(cg->st_writeback, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1317 else rrdset_next(cg->st_writeback);
1319 if(cg->memory.has_dirty_swap)
1320 rrddim_set(cg->st_writeback, "dirty", cg->memory.dirty);
1321 rrddim_set(cg->st_writeback, "writeback", cg->memory.writeback);
1322 rrdset_done(cg->st_writeback);
1324 if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
1325 if(unlikely(!cg->st_mem_activity)) {
1326 cg->st_mem_activity = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_activity");
1327 if(!cg->st_mem_activity) {
1328 snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
1329 cg->st_mem_activity = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s", 40400, update_every, RRDSET_TYPE_LINE);
1331 rrddim_add(cg->st_mem_activity, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1332 rrddim_add(cg->st_mem_activity, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1335 else rrdset_next(cg->st_mem_activity);
1337 rrddim_set(cg->st_mem_activity, "pgpgin", cg->memory.pgpgin);
1338 rrddim_set(cg->st_mem_activity, "pgpgout", cg->memory.pgpgout);
1339 rrdset_done(cg->st_mem_activity);
1342 if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
1343 if(unlikely(!cg->st_pgfaults)) {
1344 cg->st_pgfaults = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "pgfaults");
1345 if(!cg->st_pgfaults) {
1346 snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
1347 cg->st_pgfaults = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500, update_every, RRDSET_TYPE_LINE);
1349 rrddim_add(cg->st_pgfaults, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1350 rrddim_add(cg->st_pgfaults, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
1353 else rrdset_next(cg->st_pgfaults);
1355 rrddim_set(cg->st_pgfaults, "pgfault", cg->memory.pgfault);
1356 rrddim_set(cg->st_pgfaults, "pgmajfault", cg->memory.pgmajfault);
1357 rrdset_done(cg->st_pgfaults);
1361 if(cg->memory.usage_in_bytes_updated) {
1362 if(unlikely(!cg->st_mem_usage)) {
1363 cg->st_mem_usage = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_usage");
1364 if(!cg->st_mem_usage) {
1365 snprintfz(title, CHART_TITLE_MAX, "Total Memory for cgroup %s", cg->chart_title);
1366 cg->st_mem_usage = rrdset_create(type, "mem_usage", NULL, "mem", "cgroup.mem_usage", title, "MB", 40200, update_every, RRDSET_TYPE_STACKED);
1368 rrddim_add(cg->st_mem_usage, "ram", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1369 rrddim_add(cg->st_mem_usage, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
1372 else rrdset_next(cg->st_mem_usage);
1374 rrddim_set(cg->st_mem_usage, "ram", cg->memory.usage_in_bytes);
1375 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);
1376 rrdset_done(cg->st_mem_usage);
1379 if(cg->memory.failcnt_updated && cg->memory.failcnt > 0) {
1380 if(unlikely(!cg->st_mem_failcnt)) {
1381 cg->st_mem_failcnt = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "mem_failcnt");
1382 if(!cg->st_mem_failcnt) {
1383 snprintfz(title, CHART_TITLE_MAX, "Memory Limit Failures for cgroup %s", cg->chart_title);
1384 cg->st_mem_failcnt = rrdset_create(type, "mem_failcnt", NULL, "mem", "cgroup.mem_failcnt", title, "MB", 40250, update_every, RRDSET_TYPE_LINE);
1386 rrddim_add(cg->st_mem_failcnt, "failures", NULL, 1, 1, RRDDIM_INCREMENTAL);
1389 else rrdset_next(cg->st_mem_failcnt);
1391 rrddim_set(cg->st_mem_failcnt, "failures", cg->memory.failcnt);
1392 rrdset_done(cg->st_mem_failcnt);
1395 if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
1396 if(unlikely(!cg->st_io)) {
1397 cg->st_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "io");
1399 snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1400 cg->st_io = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1402 rrddim_add(cg->st_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1403 rrddim_add(cg->st_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1406 else rrdset_next(cg->st_io);
1408 rrddim_set(cg->st_io, "read", cg->io_service_bytes.Read);
1409 rrddim_set(cg->st_io, "write", cg->io_service_bytes.Write);
1410 rrdset_done(cg->st_io);
1413 if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
1414 if(unlikely(!cg->st_serviced_ops)) {
1415 cg->st_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "serviced_ops");
1416 if(!cg->st_serviced_ops) {
1417 snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s"
1419 cg->st_serviced_ops = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200, update_every, RRDSET_TYPE_LINE);
1421 rrddim_add(cg->st_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1422 rrddim_add(cg->st_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1425 else rrdset_next(cg->st_serviced_ops);
1427 rrddim_set(cg->st_serviced_ops, "read", cg->io_serviced.Read);
1428 rrddim_set(cg->st_serviced_ops, "write", cg->io_serviced.Write);
1429 rrdset_done(cg->st_serviced_ops);
1432 if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
1433 if(unlikely(!cg->st_throttle_io)) {
1434 cg->st_throttle_io = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_io");
1435 if(!cg->st_throttle_io) {
1436 snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
1437 cg->st_throttle_io = rrdset_create(type, "throttle_io", NULL, "disk", "cgroup.throttle_io", title, "KB/s", 41200, update_every, RRDSET_TYPE_AREA);
1439 rrddim_add(cg->st_throttle_io, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1440 rrddim_add(cg->st_throttle_io, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1443 else rrdset_next(cg->st_throttle_io);
1445 rrddim_set(cg->st_throttle_io, "read", cg->throttle_io_service_bytes.Read);
1446 rrddim_set(cg->st_throttle_io, "write", cg->throttle_io_service_bytes.Write);
1447 rrdset_done(cg->st_throttle_io);
1451 if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
1452 if(unlikely(!cg->st_throttle_serviced_ops)) {
1453 cg->st_throttle_serviced_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "throttle_serviced_ops");
1454 if(!cg->st_throttle_serviced_ops) {
1455 snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
1456 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);
1458 rrddim_add(cg->st_throttle_serviced_ops, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
1459 rrddim_add(cg->st_throttle_serviced_ops, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
1462 else rrdset_next(cg->st_throttle_serviced_ops);
1464 rrddim_set(cg->st_throttle_serviced_ops, "read", cg->throttle_io_serviced.Read);
1465 rrddim_set(cg->st_throttle_serviced_ops, "write", cg->throttle_io_serviced.Write);
1466 rrdset_done(cg->st_throttle_serviced_ops);
1469 if(cg->io_queued.updated) {
1470 if(unlikely(!cg->st_queued_ops)) {
1471 cg->st_queued_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "queued_ops");
1472 if(!cg->st_queued_ops) {
1473 snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
1474 cg->st_queued_ops = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000, update_every, RRDSET_TYPE_LINE);
1476 rrddim_add(cg->st_queued_ops, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
1477 rrddim_add(cg->st_queued_ops, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
1480 else rrdset_next(cg->st_queued_ops);
1482 rrddim_set(cg->st_queued_ops, "read", cg->io_queued.Read);
1483 rrddim_set(cg->st_queued_ops, "write", cg->io_queued.Write);
1484 rrdset_done(cg->st_queued_ops);
1487 if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
1488 if(unlikely(!cg->st_merged_ops)) {
1489 cg->st_merged_ops = rrdset_find_bytype(cgroup_chart_type(type, cg->chart_id, RRD_ID_LENGTH_MAX), "merged_ops");
1490 if(!cg->st_merged_ops) {
1491 snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
1492 cg->st_merged_ops = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100, update_every, RRDSET_TYPE_LINE);
1494 rrddim_add(cg->st_merged_ops, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
1495 rrddim_add(cg->st_merged_ops, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
1498 else rrdset_next(cg->st_merged_ops);
1500 rrddim_set(cg->st_merged_ops, "read", cg->io_merged.Read);
1501 rrddim_set(cg->st_merged_ops, "write", cg->io_merged.Write);
1502 rrdset_done(cg->st_merged_ops);
1506 debug(D_CGROUP, "done updating cgroups charts");
1509 // ----------------------------------------------------------------------------
1512 int do_sys_fs_cgroup(int update_every, usec_t dt) {
1515 static int cgroup_global_config_read = 0;
1516 static time_t last_run = 0;
1517 time_t now = now_realtime_sec();
1519 if(unlikely(!cgroup_global_config_read)) {
1520 read_cgroup_plugin_configuration();
1521 cgroup_global_config_read = 1;
1524 if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
1529 read_all_cgroups(cgroup_root);
1530 update_cgroup_charts(update_every);
1535 void *cgroups_main(void *ptr) {
1536 struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
1538 info("CGROUP Plugin thread created with task id %d", gettid());
1540 if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
1541 error("Cannot set pthread cancel type to DEFERRED.");
1543 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
1544 error("Cannot set pthread cancel state to ENABLE.");
1546 struct rusage thread;
1548 // when ZERO, attempt to do it
1549 int vdo_sys_fs_cgroup = 0;
1550 int vdo_cpu_netdata = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
1552 // keep track of the time each module was called
1553 usec_t sutime_sys_fs_cgroup = 0ULL;
1555 // the next time we will run - aligned properly
1556 usec_t sunext = (now_realtime_sec() - (now_realtime_sec() % rrd_update_every) + rrd_update_every) * USEC_PER_SEC;
1558 RRDSET *stcpu_thread = NULL;
1562 if(unlikely(netdata_exit)) break;
1564 // delay until it is our time to run
1565 while((sunow = now_realtime_usec()) < sunext)
1566 sleep_usec(sunext - sunow);
1568 // find the next time we need to run
1569 while(now_realtime_usec() > sunext)
1570 sunext += rrd_update_every * USEC_PER_SEC;
1572 if(unlikely(netdata_exit)) break;
1574 // BEGIN -- the job to be done
1576 if(!vdo_sys_fs_cgroup) {
1577 debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
1578 sunow = now_realtime_usec();
1579 vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
1580 sutime_sys_fs_cgroup = sunow;
1582 if(unlikely(netdata_exit)) break;
1584 // END -- the job is done
1586 // --------------------------------------------------------------------
1588 if(!vdo_cpu_netdata) {
1589 getrusage(RUSAGE_THREAD, &thread);
1591 if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
1593 stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "cgroups", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
1595 rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
1596 rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
1598 else rrdset_next(stcpu_thread);
1600 rrddim_set(stcpu_thread, "user" , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
1601 rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
1602 rrdset_done(stcpu_thread);
1606 info("CGROUP thread exiting");
1608 static_thread->enabled = 0;