From ac114eac058472786c5ccd52bfb812075343f00f Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Wed, 27 Sep 2017 16:52:12 +0200 Subject: [PATCH] add option to collect the count of active memory maps for linux processes --- src/collectd.conf.in | 1 + src/processes.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/types.db | 1 + 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/collectd.conf.in b/src/collectd.conf.in index bde168c1..0940c749 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -1170,6 +1170,7 @@ # # CollectFileDescriptor true # CollectContextSwitch true +# CollectMemoryMaps true # Process "name" # ProcessMatch "name" "regex" # diff --git a/src/processes.c b/src/processes.c index 727ec7fd..d2b6fee7 100644 --- a/src/processes.c +++ b/src/processes.c @@ -33,6 +33,7 @@ * Clément Stenac * Cosmin Ioiart * Pavel Rochnyack + * Wilfried Goesgens **/ #include "collectd.h" @@ -168,6 +169,7 @@ typedef struct process_entry_s { unsigned long num_proc; unsigned long num_lwp; unsigned long num_fd; + unsigned long num_maps; unsigned long vmem_size; unsigned long vmem_rss; unsigned long vmem_data; @@ -194,6 +196,8 @@ typedef struct process_entry_s { _Bool has_cswitch; _Bool has_fd; + + _Bool has_maps; } process_entry_t; typedef struct procstat_entry_s { @@ -229,6 +233,7 @@ typedef struct procstat { unsigned long num_proc; unsigned long num_lwp; unsigned long num_fd; + unsigned long num_maps; unsigned long vmem_size; unsigned long vmem_rss; unsigned long vmem_data; @@ -253,6 +258,7 @@ typedef struct procstat { derive_t cswitch_invol; _Bool report_fd_num; + _Bool report_maps_num; _Bool report_ctx_switch; struct procstat *next; @@ -264,6 +270,7 @@ static procstat_t *list_head_g = NULL; static _Bool want_init = 1; static _Bool report_ctx_switch = 0; static _Bool report_fd_num = 0; +static _Bool report_maps_num = 0; #if HAVE_THREAD_INFO static mach_port_t port_host_self; @@ -322,6 +329,7 @@ static procstat_t *ps_list_register(const char *name, const char *regexp) { new->cswitch_invol = -1; new->report_fd_num = report_fd_num; + new->report_maps_num = report_maps_num; new->report_ctx_switch = report_ctx_switch; #if HAVE_REGEX_H @@ -472,6 +480,7 @@ static void ps_list_add(const char *name, const char *cmdline, ps->num_proc += entry->num_proc; ps->num_lwp += entry->num_lwp; ps->num_fd += entry->num_fd; + ps->num_maps += entry->num_maps; ps->vmem_size += entry->vmem_size; ps->vmem_rss += entry->vmem_rss; ps->vmem_data += entry->vmem_data; @@ -521,6 +530,7 @@ static void ps_list_reset(void) { ps->num_proc = 0; ps->num_lwp = 0; ps->num_fd = 0; + ps->num_maps = 0; ps->vmem_size = 0; ps->vmem_rss = 0; ps->vmem_data = 0; @@ -561,6 +571,8 @@ static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) { cf_util_get_boolean(c, &ps->report_ctx_switch); else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) cf_util_get_boolean(c, &ps->report_fd_num); + else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) + cf_util_get_boolean(c, &ps->report_maps_num); else { ERROR("processes plugin: Option `%s' not allowed here.", c->key); } @@ -619,6 +631,8 @@ static int ps_config(oconfig_item_t *ci) { cf_util_get_boolean(c, &report_ctx_switch); } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) { cf_util_get_boolean(c, &report_fd_num); + } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) { + cf_util_get_boolean(c, &report_maps_num); } else { ERROR("processes plugin: The `%s' configuration option is not " "understood and will be ignored.", @@ -738,7 +752,7 @@ static void ps_submit_proc_list(procstat_t *ps) { plugin_dispatch_values(&vl); if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) { - sstrncpy(vl.type, "io_octets", sizeof(vl.type)); + sstrncpy(vl.type, "ps_disk_octets", sizeof(vl.type)); vl.values[0].derive = ps->io_rchar; vl.values[1].derive = ps->io_wchar; vl.values_len = 2; @@ -761,13 +775,20 @@ static void ps_submit_proc_list(procstat_t *ps) { plugin_dispatch_values(&vl); } - if (ps->num_fd > 0) { + if (ps->num_fd > 0) { sstrncpy(vl.type, "file_handles", sizeof(vl.type)); vl.values[0].gauge = ps->num_fd; vl.values_len = 1; plugin_dispatch_values(&vl); } + if (ps->num_maps > 0) { + sstrncpy(vl.type, "memory_maps", sizeof(vl.type)); + vl.values[0].gauge = ps->num_maps; + vl.values_len = 1; + plugin_dispatch_values(&vl); + } + if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) { sstrncpy(vl.type, "contextswitch", sizeof(vl.type)); sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance)); @@ -782,7 +803,7 @@ static void ps_submit_proc_list(procstat_t *ps) { plugin_dispatch_values(&vl); } - DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; " + DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; " "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; " "vmem_code = %lu; " "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; " @@ -791,11 +812,12 @@ static void ps_submit_proc_list(procstat_t *ps) { "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; " "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; " "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";", - ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->vmem_size, + ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps, ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol, ps->cswitch_invol); + } /* void ps_submit_proc_list */ #if KERNEL_LINUX || KERNEL_SOLARIS @@ -1004,6 +1026,31 @@ static int ps_read_io(process_entry_t *ps) { return 0; } /* int ps_read_io (...) */ +static int ps_count_maps(pid_t pid) { + FILE *fh; + char buffer[1024]; + char filename[64]; + int count = 0; + + snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); + if ((fh = fopen(filename, "r")) == NULL) { + DEBUG("ps_count_maps: Failed to open file `%s'", filename); + return -1; + } + + while (fgets(buffer, sizeof(buffer), fh) != NULL) { + if (strchr(buffer, '\n')) { + count ++; + } + } /* while (fgets) */ + + if (fclose(fh)) { + char errbuf[1024]; + WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + } + return count; +} /* int ps_count_maps (...) */ + static int ps_count_fd(int pid) { char dirname[64]; DIR *dh; @@ -1040,6 +1087,14 @@ static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) { } } + if (ps->report_maps_num) { + int num_maps; + if (entry->has_maps == 0 && (num_maps = ps_count_maps(entry->id)) > 0) { + entry->num_maps = num_maps; + } + entry->has_maps = 1; + } + if (ps->report_fd_num) { int num_fd; if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) { @@ -1421,6 +1476,9 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) { */ ps->num_fd = 0; + /* Number of memory mappings */ + ps->num_maps = 0; + /* * Calculating input/ouput chars * Formula used is total chars / total blocks => chars/block @@ -1656,6 +1714,9 @@ static int ps_read(void) { /* File descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + pse.vmem_minflt_counter = task_events_info.cow_faults; pse.vmem_majflt_counter = task_events_info.faults; @@ -1961,6 +2022,9 @@ static int ps_read(void) { /* file descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + /* context switch counters not implemented */ pse.cswitch_vol = -1; pse.cswitch_invol = -1; @@ -2102,6 +2166,9 @@ static int ps_read(void) { /* file descriptor count not implemented */ pse.num_fd = 0; + /* Number of memory mappings */ + pse.num_maps = 0; + /* context switch counters not implemented */ pse.cswitch_vol = -1; pse.cswitch_invol = -1; @@ -2265,6 +2332,7 @@ static int ps_read(void) { pse.io_diskw = -1; pse.num_fd = 0; + pse.num_maps = 0; pse.cswitch_vol = -1; pse.cswitch_invol = -1; diff --git a/src/types.db b/src/types.db index 577d28f1..c5bcab63 100644 --- a/src/types.db +++ b/src/types.db @@ -129,6 +129,7 @@ latency value:GAUGE:0:U links value:GAUGE:0:U load shortterm:GAUGE:0:5000, midterm:GAUGE:0:5000, longterm:GAUGE:0:5000 memory_bandwidth value:DERIVE:0:U +memory_maps value:GAUGE:0:U md_disks value:GAUGE:0:U memcached_command value:DERIVE:0:U memcached_connections value:GAUGE:0:U -- 2.11.0