13 #include "appconfig.h"
18 // ----------------------------------------------------------------------------
21 static int cgroup_enable_cpuacct_stat = CONFIG_ONDEMAND_ONDEMAND;
22 static int cgroup_enable_cpuacct_usage = CONFIG_ONDEMAND_ONDEMAND;
23 static int cgroup_enable_memory = CONFIG_ONDEMAND_ONDEMAND;
24 static int cgroup_enable_blkio = CONFIG_ONDEMAND_ONDEMAND;
25 static int cgroup_enable_new_cgroups_detected_at_runtime = 1;
26 static int cgroup_check_for_new_every = 10;
27 static char *cgroup_cpuacct_base = NULL;
28 static char *cgroup_blkio_base = NULL;
29 static char *cgroup_memory_base = NULL;
31 static int cgroup_root_count = 0;
32 static int cgroup_root_max = 50;
33 static int cgroup_max_depth = 0;
35 void read_cgroup_plugin_configuration() {
36 cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new plugin every", cgroup_check_for_new_every);
38 cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
39 cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
40 cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
41 cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
43 char filename[FILENAME_MAX + 1];
44 snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/cpuacct");
45 cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
47 snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/blkio");
48 cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
50 snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/fs/cgroup/memory");
51 cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
53 cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
54 cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
56 cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable cgroups detected after first run", cgroup_enable_new_cgroups_detected_at_runtime);
59 // ----------------------------------------------------------------------------
67 unsigned long long Read;
68 unsigned long long Write;
69 unsigned long long Sync;
70 unsigned long long Async;
71 unsigned long long Total;
74 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
80 unsigned long long cache;
81 unsigned long long rss;
82 unsigned long long rss_huge;
83 unsigned long long mapped_file;
84 unsigned long long writeback;
85 unsigned long long pgpgin;
86 unsigned long long pgpgout;
87 unsigned long long pgfault;
88 unsigned long long pgmajfault;
89 /* unsigned long long inactive_anon;
90 unsigned long long active_anon;
91 unsigned long long inactive_file;
92 unsigned long long active_file;
93 unsigned long long unevictable;
94 unsigned long long hierarchical_memory_limit;
95 unsigned long long total_cache;
96 unsigned long long total_rss;
97 unsigned long long total_rss_huge;
98 unsigned long long total_mapped_file;
99 unsigned long long total_writeback;
100 unsigned long long total_pgpgin;
101 unsigned long long total_pgpgout;
102 unsigned long long total_pgfault;
103 unsigned long long total_pgmajfault;
104 unsigned long long total_inactive_anon;
105 unsigned long long total_active_anon;
106 unsigned long long total_inactive_file;
107 unsigned long long total_active_file;
108 unsigned long long total_unevictable;
112 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
113 struct cpuacct_stat {
118 unsigned long long user;
119 unsigned long long system;
122 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
123 struct cpuacct_usage {
129 unsigned long long *cpu_percpu;
141 struct cpuacct_stat cpuacct_stat;
142 struct cpuacct_usage cpuacct_usage;
144 struct memory memory;
146 struct blkio io_service_bytes; // bytes
147 struct blkio io_serviced; // operations
149 struct blkio throttle_io_service_bytes; // bytes
150 struct blkio throttle_io_serviced; // operations
152 struct blkio io_merged; // operations
153 struct blkio io_queued; // operations
157 } *cgroup_root = NULL;
159 // ----------------------------------------------------------------------------
160 // read values from /sys
162 void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
163 static procfile *ff = NULL;
165 static uint32_t user_hash = 0;
166 static uint32_t system_hash = 0;
168 if(unlikely(user_hash == 0)) {
169 user_hash = simple_hash("user");
170 system_hash = simple_hash("system");
175 ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
178 ff = procfile_readall(ff);
181 unsigned long i, lines = procfile_lines(ff);
184 error("File '%s' should have 1+ lines.", cp->filename);
188 for(i = 0; i < lines ; i++) {
189 char *s = procfile_lineword(ff, i, 0);
190 uint32_t hash = simple_hash(s);
192 if(hash == user_hash && !strcmp(s, "user"))
193 cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
195 else if(hash == system_hash && !strcmp(s, "system"))
196 cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
201 // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
205 void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
206 static procfile *ff = NULL;
210 ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
213 ff = procfile_readall(ff);
216 if(procfile_lines(ff) < 1) {
217 error("File '%s' should have 1+ lines but has %d.", ca->filename, procfile_lines(ff));
221 unsigned long i = procfile_linewords(ff, 0);
224 // we may have 1 more CPU reported
226 char *s = procfile_lineword(ff, 0, i - 1);
232 free(ca->cpu_percpu);
234 ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
236 fatal("Cannot allocate memory (%z bytes)", sizeof(unsigned long long) * i);
240 for(i = 0; i < ca->cpus ;i++) {
241 ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
242 // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
249 void cgroup_read_blkio(struct blkio *io) {
250 static procfile *ff = NULL;
252 static uint32_t Read_hash = 0;
253 static uint32_t Write_hash = 0;
254 static uint32_t Sync_hash = 0;
255 static uint32_t Async_hash = 0;
256 static uint32_t Total_hash = 0;
258 if(unlikely(Read_hash == 0)) {
259 Read_hash = simple_hash("Read");
260 Write_hash = simple_hash("Write");
261 Sync_hash = simple_hash("Sync");
262 Async_hash = simple_hash("Async");
263 Total_hash = simple_hash("Total");
268 ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
271 ff = procfile_readall(ff);
274 unsigned long i, lines = procfile_lines(ff);
277 error("File '%s' should have 1+ lines.", io->filename);
287 for(i = 0; i < lines ; i++) {
288 char *s = procfile_lineword(ff, i, 1);
289 uint32_t hash = simple_hash(s);
291 if(hash == Read_hash && !strcmp(s, "Read"))
292 io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
294 else if(hash == Write_hash && !strcmp(s, "Write"))
295 io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
297 else if(hash == Sync_hash && !strcmp(s, "Sync"))
298 io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
300 else if(hash == Async_hash && !strcmp(s, "Async"))
301 io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
303 else if(hash == Total_hash && !strcmp(s, "Total"))
304 io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
308 // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
312 void cgroup_read_memory(struct memory *mem) {
313 static procfile *ff = NULL;
315 static uint32_t cache_hash = 0;
316 static uint32_t rss_hash = 0;
317 static uint32_t rss_huge_hash = 0;
318 static uint32_t mapped_file_hash = 0;
319 static uint32_t writeback_hash = 0;
320 static uint32_t pgpgin_hash = 0;
321 static uint32_t pgpgout_hash = 0;
322 static uint32_t pgfault_hash = 0;
323 static uint32_t pgmajfault_hash = 0;
324 /* static uint32_t inactive_anon_hash = 0;
325 static uint32_t active_anon_hash = 0;
326 static uint32_t inactive_file_hash = 0;
327 static uint32_t active_file_hash = 0;
328 static uint32_t unevictable_hash = 0;
329 static uint32_t hierarchical_memory_limit_hash = 0;
330 static uint32_t total_cache_hash = 0;
331 static uint32_t total_rss_hash = 0;
332 static uint32_t total_rss_huge_hash = 0;
333 static uint32_t total_mapped_file_hash = 0;
334 static uint32_t total_writeback_hash = 0;
335 static uint32_t total_pgpgin_hash = 0;
336 static uint32_t total_pgpgout_hash = 0;
337 static uint32_t total_pgfault_hash = 0;
338 static uint32_t total_pgmajfault_hash = 0;
339 static uint32_t total_inactive_anon_hash = 0;
340 static uint32_t total_active_anon_hash = 0;
341 static uint32_t total_inactive_file_hash = 0;
342 static uint32_t total_active_file_hash = 0;
343 static uint32_t total_unevictable_hash = 0;
345 if(unlikely(cache_hash == 0)) {
346 cache_hash = simple_hash("cache");
347 rss_hash = simple_hash("rss");
348 rss_huge_hash = simple_hash("rss_huge");
349 mapped_file_hash = simple_hash("mapped_file");
350 writeback_hash = simple_hash("writeback");
351 pgpgin_hash = simple_hash("pgpgin");
352 pgpgout_hash = simple_hash("pgpgout");
353 pgfault_hash = simple_hash("pgfault");
354 pgmajfault_hash = simple_hash("pgmajfault");
355 /* inactive_anon_hash = simple_hash("inactive_anon");
356 active_anon_hash = simple_hash("active_anon");
357 inactive_file_hash = simple_hash("inactive_file");
358 active_file_hash = simple_hash("active_file");
359 unevictable_hash = simple_hash("unevictable");
360 hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
361 total_cache_hash = simple_hash("total_cache");
362 total_rss_hash = simple_hash("total_rss");
363 total_rss_huge_hash = simple_hash("total_rss_huge");
364 total_mapped_file_hash = simple_hash("total_mapped_file");
365 total_writeback_hash = simple_hash("total_writeback");
366 total_pgpgin_hash = simple_hash("total_pgpgin");
367 total_pgpgout_hash = simple_hash("total_pgpgout");
368 total_pgfault_hash = simple_hash("total_pgfault");
369 total_pgmajfault_hash = simple_hash("total_pgmajfault");
370 total_inactive_anon_hash = simple_hash("total_inactive_anon");
371 total_active_anon_hash = simple_hash("total_active_anon");
372 total_inactive_file_hash = simple_hash("total_inactive_file");
373 total_active_file_hash = simple_hash("total_active_file");
374 total_unevictable_hash = simple_hash("total_unevictable");
380 ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
383 ff = procfile_readall(ff);
386 unsigned long i, lines = procfile_lines(ff);
389 error("File '%s' should have 1+ lines.", mem->filename);
393 for(i = 0; i < lines ; i++) {
394 char *s = procfile_lineword(ff, i, 0);
395 uint32_t hash = simple_hash(s);
397 if(hash == cache_hash && !strcmp(s, "cache"))
398 mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
400 else if(hash == rss_hash && !strcmp(s, "rss"))
401 mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
403 else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
404 mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
406 else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
407 mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
409 else if(hash == writeback_hash && !strcmp(s, "writeback"))
410 mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
412 else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
413 mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
415 else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
416 mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
418 else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
419 mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
421 else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
422 mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
424 /* else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
425 mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
427 else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
428 mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
430 else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
431 mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
433 else if(hash == active_file_hash && !strcmp(s, "active_file"))
434 mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
436 else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
437 mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
439 else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
440 mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
442 else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
443 mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
445 else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
446 mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
448 else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
449 mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
451 else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
452 mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
454 else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
455 mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
457 else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
458 mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
460 else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
461 mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
463 else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
464 mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
466 else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
467 mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
469 else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
470 mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
472 else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
473 mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
475 else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
476 mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
478 else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
479 mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
481 else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
482 mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
486 // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
492 void cgroup_read(struct cgroup *cg) {
493 cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
494 cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
495 cgroup_read_memory(&cg->memory);
496 cgroup_read_blkio(&cg->io_service_bytes);
497 cgroup_read_blkio(&cg->io_serviced);
498 cgroup_read_blkio(&cg->throttle_io_service_bytes);
499 cgroup_read_blkio(&cg->throttle_io_serviced);
500 cgroup_read_blkio(&cg->io_merged);
501 cgroup_read_blkio(&cg->io_queued);
504 void read_all_cgroups(struct cgroup *cg) {
507 for(i = cg; i ; i = i->next)
511 // ----------------------------------------------------------------------------
512 // add/remove/find cgroup objects
514 struct cgroup *cgroup_add(const char *id) {
515 if(cgroup_root_count >= cgroup_root_max) {
516 info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
520 int def = cgroup_enable_new_cgroups_detected_at_runtime;
521 const char *name = id;
525 // disable by default the host cgroup
529 if(*name == '/') name++;
531 // disable by default the parent cgroup
532 // for known cgroup managers
533 if(!strcmp(name, "lxc") || !strcmp(name, "docker"))
537 char option[FILENAME_MAX + 1];
538 snprintf(option, FILENAME_MAX, "enable cgroup %s", name);
539 if(!config_get_boolean("plugin:cgroups", option, def))
542 struct cgroup *cg = calloc(1, sizeof(struct cgroup));
545 if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
547 cg->hash = simple_hash(cg->id);
549 cg->name = strdup(name);
550 if(!cg->name) fatal("Cannot allocate memory for cgroup '%s'", id);
557 for(e = cgroup_root; e->next ;e = e->next) ;
563 // fprintf(stderr, " > added cgroup No %d, with id '%s' (%u) and name '%s'\n", cgroup_root_count, cg->id, cg->hash, cg->name);
565 else fatal("Cannot allocate memory for cgroup '%s'", id);
570 void cgroup_remove(struct cgroup *cg) {
571 if(cg == cgroup_root) {
572 cgroup_root = cg->next;
576 for(e = cgroup_root; e->next ;e = e->next)
577 if(unlikely(e->next == cg)) break;
580 error("Cannot find cgroup '%s' in list of cgroups", cg->id);
588 free(cg->cpuacct_usage.cpu_percpu);
590 free(cg->cpuacct_stat.filename);
591 free(cg->cpuacct_usage.filename);
592 free(cg->memory.filename);
593 free(cg->io_service_bytes.filename);
594 free(cg->io_serviced.filename);
595 free(cg->throttle_io_service_bytes.filename);
596 free(cg->throttle_io_serviced.filename);
597 free(cg->io_merged.filename);
598 free(cg->io_queued.filename);
605 // find if a given cgroup exists
606 struct cgroup *cgroup_find(const char *id) {
607 uint32_t hash = simple_hash(id);
609 // fprintf(stderr, " > searching for '%s' (%u)\n", id, hash);
612 for(cg = cgroup_root; cg ; cg = cg->next) {
613 if(hash == cg->hash && strcmp(id, cg->id) == 0)
620 // ----------------------------------------------------------------------------
621 // detect running cgroups
623 // callback for find_file_in_subdirs()
624 void found_dir_in_subdir(const char *dir) {
625 // fprintf(stderr, "found dir '%s'\n", dir);
627 struct cgroup *cg = cgroup_find(dir);
629 if(*dir && cgroup_max_depth > 0) {
633 for(s = dir; *s ;s++)
634 if(unlikely(*s == '/'))
637 if(depth > cgroup_max_depth) {
638 info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
642 cg = cgroup_add(dir);
645 if(cg) cg->available = 1;
648 void find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
649 if(!this) this = base;
650 size_t dirlen = strlen(this), baselen = strlen(base);
652 DIR *dir = opendir(this);
655 callback(&this[baselen]);
657 struct dirent *de = NULL;
658 while((de = readdir(dir))) {
659 if(de->d_type == DT_DIR
661 (de->d_name[0] == '.' && de->d_name[1] == '\0')
662 || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
666 // fprintf(stderr, "examining '%s/%s'\n", this, de->d_name);
668 if(de->d_type == DT_DIR) {
669 char *s = malloc(dirlen + strlen(de->d_name) + 2);
673 strcat(s, de->d_name);
674 find_dir_in_subdirs(base, s, callback);
683 void mark_all_cgroups_as_not_available() {
686 // mark all as not available
687 for(cg = cgroup_root; cg ; cg = cg->next)
691 struct cgroup *find_all_cgroups() {
693 mark_all_cgroups_as_not_available();
695 if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
696 find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_dir_in_subdir);
698 if(cgroup_enable_blkio)
699 find_dir_in_subdirs(cgroup_blkio_base, NULL, found_dir_in_subdir);
701 if(cgroup_enable_memory)
702 find_dir_in_subdirs(cgroup_memory_base, NULL, found_dir_in_subdir);
705 for(cg = cgroup_root; cg ; cg = cg->next) {
706 // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
713 // check for newly added cgroups
714 // and update the filenames they read
715 char filename[FILENAME_MAX + 1];
716 if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
717 snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
718 cg->cpuacct_stat.filename = strdup(filename);
720 if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
721 snprintf(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
722 cg->cpuacct_usage.filename = strdup(filename);
724 if(cgroup_enable_memory && !cg->memory.filename) {
725 snprintf(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
726 cg->memory.filename = strdup(filename);
728 if(cgroup_enable_blkio) {
729 if(!cg->io_service_bytes.filename) {
730 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
731 cg->io_service_bytes.filename = strdup(filename);
733 if(!cg->io_serviced.filename) {
734 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
735 cg->io_serviced.filename = strdup(filename);
737 if(!cg->throttle_io_service_bytes.filename) {
738 snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
739 cg->throttle_io_service_bytes.filename = strdup(filename);
741 if(!cg->throttle_io_serviced.filename) {
742 snprintf(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
743 cg->throttle_io_serviced.filename = strdup(filename);
745 if(!cg->io_merged.filename) {
746 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
747 cg->io_merged.filename = strdup(filename);
749 if(!cg->io_queued.filename) {
750 snprintf(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
751 cg->io_queued.filename = strdup(filename);
759 // ----------------------------------------------------------------------------
762 #define CHART_TITLE_MAX 300
764 void update_cgroup_charts(int update_every) {
765 char type[RRD_ID_LENGTH_MAX + 1];
766 char title[CHART_TITLE_MAX + 1];
771 for(cg = cgroup_root; cg ; cg = cg->next) {
772 if(!cg->available) continue;
774 if(cg->id[0] == '\0')
775 strcpy(type, "cgroup_host");
776 else if(cg->id[0] == '/')
777 snprintf(type, RRD_ID_LENGTH_MAX, "cgroup%s", cg->id);
779 snprintf(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->id);
781 netdata_fix_chart_id(type);
783 if(cg->cpuacct_stat.updated) {
784 st = rrdset_find_bytype(type, "cpu");
786 snprintf(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->name);
787 st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
789 rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
790 rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
792 else rrdset_next(st);
794 rrddim_set(st, "user", cg->cpuacct_stat.user);
795 rrddim_set(st, "system", cg->cpuacct_stat.system);
799 if(cg->cpuacct_usage.updated) {
800 char id[RRD_ID_LENGTH_MAX + 1];
803 st = rrdset_find_bytype(type, "cpu_per_core");
805 snprintf(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->name);
806 st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
808 for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
809 snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
810 rrddim_add(st, id, NULL, 100, 1000000, RRDDIM_INCREMENTAL);
813 else rrdset_next(st);
815 for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
816 snprintf(id, CHART_TITLE_MAX, "cpu%d", i);
817 rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
822 if(cg->memory.updated) {
823 if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
824 st = rrdset_find_bytype(type, "mem");
826 snprintf(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->name);
827 st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
828 RRDSET_TYPE_STACKED);
830 rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
831 rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
832 rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
833 rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
835 else rrdset_next(st);
837 rrddim_set(st, "cache", cg->memory.cache);
838 rrddim_set(st, "rss", cg->memory.rss);
839 rrddim_set(st, "rss_huge", cg->memory.rss_huge);
840 rrddim_set(st, "mapped_file", cg->memory.mapped_file);
844 st = rrdset_find_bytype(type, "writeback");
846 snprintf(title, CHART_TITLE_MAX, "Writable Memory for cgroup %s", cg->name);
847 st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
848 update_every, RRDSET_TYPE_AREA);
850 rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
852 else rrdset_next(st);
854 rrddim_set(st, "writeback", cg->memory.writeback);
857 if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
858 st = rrdset_find_bytype(type, "mem_activity");
860 snprintf(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->name);
861 st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
862 40400, update_every, RRDSET_TYPE_LINE);
864 rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
865 rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
867 else rrdset_next(st);
869 rrddim_set(st, "pgpgin", cg->memory.pgpgin);
870 rrddim_set(st, "pgpgout", cg->memory.pgpgout);
874 if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
875 st = rrdset_find_bytype(type, "pgfaults");
877 snprintf(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->name);
878 st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
879 update_every, RRDSET_TYPE_LINE);
881 rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
882 rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
884 else rrdset_next(st);
886 rrddim_set(st, "pgfault", cg->memory.pgfault);
887 rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
892 if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
893 st = rrdset_find_bytype(type, "io");
895 snprintf(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->name);
896 st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
897 update_every, RRDSET_TYPE_LINE);
899 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
900 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
902 else rrdset_next(st);
904 rrddim_set(st, "read", cg->io_service_bytes.Read);
905 rrddim_set(st, "write", cg->io_service_bytes.Write);
909 if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
910 st = rrdset_find_bytype(type, "serviced_ops");
912 snprintf(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->name);
913 st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
914 update_every, RRDSET_TYPE_LINE);
916 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
917 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
919 else rrdset_next(st);
921 rrddim_set(st, "read", cg->io_serviced.Read);
922 rrddim_set(st, "write", cg->io_serviced.Write);
926 if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
927 st = rrdset_find_bytype(type, "io");
929 snprintf(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->name);
930 st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
931 update_every, RRDSET_TYPE_LINE);
933 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
934 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
936 else rrdset_next(st);
938 rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
939 rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
944 if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
945 st = rrdset_find_bytype(type, "throttle_serviced_ops");
947 snprintf(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->name);
948 st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
949 update_every, RRDSET_TYPE_LINE);
951 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
952 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
954 else rrdset_next(st);
956 rrddim_set(st, "read", cg->throttle_io_serviced.Read);
957 rrddim_set(st, "write", cg->throttle_io_serviced.Write);
961 if(cg->io_queued.updated) {
962 st = rrdset_find_bytype(type, "queued_ops");
964 snprintf(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->name);
965 st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
966 update_every, RRDSET_TYPE_LINE);
968 rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
969 rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
971 else rrdset_next(st);
973 rrddim_set(st, "read", cg->io_queued.Read);
974 rrddim_set(st, "write", cg->io_queued.Write);
978 if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
979 st = rrdset_find_bytype(type, "merged_ops");
981 snprintf(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->name);
982 st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
983 update_every, RRDSET_TYPE_LINE);
985 rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
986 rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
988 else rrdset_next(st);
990 rrddim_set(st, "read", cg->io_merged.Read);
991 rrddim_set(st, "write", cg->io_merged.Write);
997 // ----------------------------------------------------------------------------
1000 int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
1001 static int cgroup_global_config_read = 0;
1002 static time_t last_run = 0;
1003 time_t now = time(NULL);
1007 if(unlikely(!cgroup_global_config_read)) {
1008 read_cgroup_plugin_configuration();
1009 cgroup_global_config_read = 1;
1012 if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
1017 read_all_cgroups(cgroup_root);
1018 update_cgroup_charts(update_every);