add option to collect the count of active memory maps for linux processes
authorWilfried Goesgens <willi@arangodb.com>
Wed, 27 Sep 2017 14:52:12 +0000 (16:52 +0200)
committerWilfried Goesgens <willi@arangodb.com>
Wed, 27 Sep 2017 14:52:12 +0000 (16:52 +0200)
src/collectd.conf.in
src/processes.c
src/types.db

index bde168c..0940c74 100644 (file)
 #<Plugin processes>
 #      CollectFileDescriptor true
 #      CollectContextSwitch true
+#       CollectMemoryMaps true
 #      Process "name"
 #      ProcessMatch "name" "regex"
 #      <Process "collectd">
index 727ec7f..d2b6fee 100644 (file)
@@ -33,6 +33,7 @@
  *   ClĂ©ment Stenac <clement.stenac at diwi.org>
  *   Cosmin Ioiart <cioiart at gmail.com>
  *   Pavel Rochnyack <pavel2000 at ngs.ru>
+ *   Wilfried Goesgens <dothebart at citadel.org>
  **/
 
 #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;
index 577d28f..c5bcab6 100644 (file)
@@ -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