Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / processes.c
index 87c96c8..f556912 100644 (file)
@@ -32,6 +32,8 @@
  *   Manuel Sanmartin
  *   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"
 #endif
 #endif
 
-typedef struct procstat_entry_s {
+#define PROCSTAT_NAME_LEN 256
+typedef struct process_entry_s {
   unsigned long id;
-  unsigned long age;
+  char name[PROCSTAT_NAME_LEN];
 
   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;
@@ -182,16 +187,43 @@ typedef struct procstat_entry_s {
   derive_t io_wchar;
   derive_t io_syscr;
   derive_t io_syscw;
+  derive_t io_diskr;
+  derive_t io_diskw;
   _Bool has_io;
 
   derive_t cswitch_vol;
   derive_t cswitch_invol;
   _Bool has_cswitch;
 
+  _Bool has_fd;
+
+  _Bool has_maps;
+} process_entry_t;
+
+typedef struct procstat_entry_s {
+  unsigned long id;
+  unsigned long age;
+
+  derive_t vmem_minflt_counter;
+  derive_t vmem_majflt_counter;
+
+  derive_t cpu_user_counter;
+  derive_t cpu_system_counter;
+
+  /* io data */
+  derive_t io_rchar;
+  derive_t io_wchar;
+  derive_t io_syscr;
+  derive_t io_syscw;
+  derive_t io_diskr;
+  derive_t io_diskw;
+
+  derive_t cswitch_vol;
+  derive_t cswitch_invol;
+
   struct procstat_entry_s *next;
 } procstat_entry_t;
 
-#define PROCSTAT_NAME_LEN 256
 typedef struct procstat {
   char name[PROCSTAT_NAME_LEN];
 #if HAVE_REGEX_H
@@ -200,6 +232,8 @@ 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;
@@ -217,10 +251,16 @@ typedef struct procstat {
   derive_t io_wchar;
   derive_t io_syscr;
   derive_t io_syscw;
+  derive_t io_diskr;
+  derive_t io_diskw;
 
   derive_t cswitch_vol;
   derive_t cswitch_invol;
 
+  _Bool report_fd_num;
+  _Bool report_maps_num;
+  _Bool report_ctx_switch;
+
   struct procstat *next;
   struct procstat_entry_s *instances;
 } procstat_t;
@@ -229,6 +269,8 @@ 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;
@@ -240,7 +282,7 @@ static mach_msg_type_number_t pset_list_len;
 
 #elif KERNEL_LINUX
 static long pagesize_g;
-static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry);
+static void ps_fill_details(const procstat_t *ps, process_entry_t *entry);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
@@ -265,7 +307,7 @@ int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
 /* put name of process from config to list_head_g tree
  * list_head_g is a list of 'procstat_t' structs with
  * processes names we want to watch */
-static void ps_list_register(const char *name, const char *regexp) {
+static procstat_t *ps_list_register(const char *name, const char *regexp) {
   procstat_t *new;
   procstat_t *ptr;
   int status;
@@ -273,10 +315,23 @@ static void ps_list_register(const char *name, const char *regexp) {
   new = calloc(1, sizeof(*new));
   if (new == NULL) {
     ERROR("processes plugin: ps_list_register: calloc failed.");
-    return;
+    return NULL;
   }
   sstrncpy(new->name, name, sizeof(new->name));
 
+  new->io_rchar = -1;
+  new->io_wchar = -1;
+  new->io_syscr = -1;
+  new->io_syscw = -1;
+  new->io_diskr = -1;
+  new->io_diskw = -1;
+  new->cswitch_vol = -1;
+  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
   if (regexp != NULL) {
     DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
@@ -285,7 +340,7 @@ static void ps_list_register(const char *name, const char *regexp) {
     if (new->re == NULL) {
       ERROR("processes plugin: ps_list_register: malloc failed.");
       sfree(new);
-      return;
+      return NULL;
     }
 
     status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
@@ -294,7 +349,7 @@ static void ps_list_register(const char *name, const char *regexp) {
             regexp);
       sfree(new->re);
       sfree(new);
-      return;
+      return NULL;
     }
   }
 #else
@@ -305,7 +360,7 @@ static void ps_list_register(const char *name, const char *regexp) {
           "has been disabled at compile time.",
           regexp);
     sfree(new);
-    return;
+    return NULL;
   }
 #endif
 
@@ -320,7 +375,7 @@ static void ps_list_register(const char *name, const char *regexp) {
       sfree(new->re);
 #endif
       sfree(new);
-      return;
+      return NULL;
     }
 
     if (ptr->next == NULL)
@@ -331,6 +386,8 @@ static void ps_list_register(const char *name, const char *regexp) {
     list_head_g = new;
   else
     ptr->next = new;
+
+  return new;
 } /* void ps_list_register */
 
 /* try to match name against entry, returns 1 if success */
@@ -352,13 +409,13 @@ static int ps_list_match(const char *name, const char *cmdline,
                      /* pmatch = */ NULL,
                      /* eflags = */ 0);
     if (status == 0)
-      return (1);
+      return 1;
   } else
 #endif
       if (strcmp(ps->name, name) == 0)
-    return (1);
+    return 1;
 
-  return (0);
+  return 0;
 } /* int ps_list_match */
 
 static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
@@ -375,13 +432,16 @@ static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
   else
     curr_value = new_counter - *curr_counter;
 
+  if (*group_counter == -1)
+    *group_counter = 0;
+
   *curr_counter = new_counter;
   *group_counter += curr_value;
 }
 
 /* add process entry to 'instances' of process 'name' (or refresh it) */
 static void ps_list_add(const char *name, const char *cmdline,
-                        procstat_entry_t *entry) {
+                        process_entry_t *entry) {
   procstat_entry_t *pse;
 
   if (entry->id == 0)
@@ -416,35 +476,38 @@ static void ps_list_add(const char *name, const char *cmdline,
     }
 
     pse->age = 0;
-    pse->num_proc = entry->num_proc;
-    pse->num_lwp = entry->num_lwp;
-    pse->vmem_size = entry->vmem_size;
-    pse->vmem_rss = entry->vmem_rss;
-    pse->vmem_data = entry->vmem_data;
-    pse->vmem_code = entry->vmem_code;
-    pse->stack_size = entry->stack_size;
-    pse->io_rchar = entry->io_rchar;
-    pse->io_wchar = entry->io_wchar;
-    pse->io_syscr = entry->io_syscr;
-    pse->io_syscw = entry->io_syscw;
-    pse->cswitch_vol = entry->cswitch_vol;
-    pse->cswitch_invol = entry->cswitch_invol;
-
-    ps->num_proc += pse->num_proc;
-    ps->num_lwp += pse->num_lwp;
-    ps->vmem_size += pse->vmem_size;
-    ps->vmem_rss += pse->vmem_rss;
-    ps->vmem_data += pse->vmem_data;
-    ps->vmem_code += pse->vmem_code;
-    ps->stack_size += pse->stack_size;
-
-    ps->io_rchar += ((pse->io_rchar == -1) ? 0 : pse->io_rchar);
-    ps->io_wchar += ((pse->io_wchar == -1) ? 0 : pse->io_wchar);
-    ps->io_syscr += ((pse->io_syscr == -1) ? 0 : pse->io_syscr);
-    ps->io_syscw += ((pse->io_syscw == -1) ? 0 : pse->io_syscw);
-
-    ps->cswitch_vol += ((pse->cswitch_vol == -1) ? 0 : pse->cswitch_vol);
-    ps->cswitch_invol += ((pse->cswitch_invol == -1) ? 0 : pse->cswitch_invol);
+
+    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;
+    ps->vmem_code += entry->vmem_code;
+    ps->stack_size += entry->stack_size;
+
+    if ((entry->io_rchar != -1) && (entry->io_wchar != -1)) {
+      ps_update_counter(&ps->io_rchar, &pse->io_rchar, entry->io_rchar);
+      ps_update_counter(&ps->io_wchar, &pse->io_wchar, entry->io_wchar);
+    }
+
+    if ((entry->io_syscr != -1) && (entry->io_syscw != -1)) {
+      ps_update_counter(&ps->io_syscr, &pse->io_syscr, entry->io_syscr);
+      ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
+    }
+
+    if ((entry->io_diskr != -1) && (entry->io_diskw != -1)) {
+      ps_update_counter(&ps->io_diskr, &pse->io_diskr, entry->io_diskr);
+      ps_update_counter(&ps->io_diskw, &pse->io_diskw, entry->io_diskw);
+    }
+
+    if ((entry->cswitch_vol != -1) && (entry->cswitch_invol != -1)) {
+      ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
+                        entry->cswitch_vol);
+      ps_update_counter(&ps->cswitch_invol, &pse->cswitch_invol,
+                        entry->cswitch_invol);
+    }
 
     ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
                       entry->vmem_minflt_counter);
@@ -466,17 +529,13 @@ static void ps_list_reset(void) {
   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
     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;
     ps->vmem_code = 0;
     ps->stack_size = 0;
-    ps->io_rchar = -1;
-    ps->io_wchar = -1;
-    ps->io_syscr = -1;
-    ps->io_syscw = -1;
-    ps->cswitch_vol = -1;
-    ps->cswitch_invol = -1;
 
     pse_prev = NULL;
     pse = ps->instances;
@@ -504,6 +563,22 @@ static void ps_list_reset(void) {
   }   /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
 }
 
+static void ps_tune_instance(oconfig_item_t *ci, procstat_t *ps) {
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *c = ci->children + i;
+
+    if (strcasecmp(c->key, "CollectContextSwitch") == 0)
+      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);
+    }
+  } /* for (ci->children) */
+} /* void ps_tune_instance */
+
 /* put all pre-defined 'Process' names from config to list_head_g tree */
 static int ps_config(oconfig_item_t *ci) {
 #if KERNEL_LINUX
@@ -512,6 +587,8 @@ static int ps_config(oconfig_item_t *ci) {
   const size_t max_procname_len = MAXCOMLEN - 1;
 #endif
 
+  procstat_t *ps;
+
   for (int i = 0; i < ci->children_num; ++i) {
     oconfig_item_t *c = ci->children + i;
 
@@ -523,13 +600,6 @@ static int ps_config(oconfig_item_t *ci) {
         continue;
       }
 
-      if (c->children_num != 0) {
-        WARNING("processes plugin: the `Process' config option "
-                "does not expect any child elements -- ignoring "
-                "content (%i elements) of the <Process '%s'> block.",
-                c->children_num, c->values[0].value.string);
-      }
-
 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
       if (strlen(c->values[0].value.string) > max_procname_len) {
         WARNING("processes plugin: this platform has a %zu character limit "
@@ -539,7 +609,10 @@ static int ps_config(oconfig_item_t *ci) {
       }
 #endif
 
-      ps_list_register(c->values[0].value.string, NULL);
+      ps = ps_list_register(c->values[0].value.string, NULL);
+
+      if (c->children_num != 0 && ps != NULL)
+        ps_tune_instance(c, ps);
     } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
       if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
           (OCONFIG_TYPE_STRING != c->values[1].type)) {
@@ -549,18 +622,17 @@ static int ps_config(oconfig_item_t *ci) {
         continue;
       }
 
-      if (c->children_num != 0) {
-        WARNING("processes plugin: the `ProcessMatch' config option "
-                "does not expect any child elements -- ignoring "
-                "content (%i elements) of the <ProcessMatch '%s' '%s'> "
-                "block.",
-                c->children_num, c->values[0].value.string,
-                c->values[1].value.string);
-      }
+      ps = ps_list_register(c->values[0].value.string,
+                            c->values[1].value.string);
 
-      ps_list_register(c->values[0].value.string, c->values[1].value.string);
+      if (c->children_num != 0 && ps != NULL)
+        ps_tune_instance(c, ps);
     } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
       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.",
@@ -569,7 +641,7 @@ static int ps_config(oconfig_item_t *ci) {
     }
   }
 
-  return (0);
+  return 0;
 }
 
 static int ps_init(void) {
@@ -591,7 +663,7 @@ static int ps_init(void) {
     ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
     pset_list = NULL;
     pset_list_len = 0;
-    return (-1);
+    return -1;
   }
 /* #endif HAVE_THREAD_INFO */
 
@@ -610,7 +682,7 @@ static int ps_init(void) {
   pagesize = getpagesize();
 #endif /* HAVE_PROCINFO_H */
 
-  return (0);
+  return 0;
 } /* int ps_init */
 
 /* submit global state (e.g.: qty of zombies, running, etc..) */
@@ -680,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, "ps_disk_octets", sizeof(vl.type));
+    sstrncpy(vl.type, "io_octets", sizeof(vl.type));
     vl.values[0].derive = ps->io_rchar;
     vl.values[1].derive = ps->io_wchar;
     vl.values_len = 2;
@@ -688,14 +760,37 @@ static void ps_submit_proc_list(procstat_t *ps) {
   }
 
   if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
-    sstrncpy(vl.type, "ps_disk_ops", sizeof(vl.type));
+    sstrncpy(vl.type, "io_ops", sizeof(vl.type));
     vl.values[0].derive = ps->io_syscr;
     vl.values[1].derive = ps->io_syscw;
     vl.values_len = 2;
     plugin_dispatch_values(&vl);
   }
 
-  if (report_ctx_switch) {
+  if ((ps->io_diskr != -1) && (ps->io_diskw != -1)) {
+    sstrncpy(vl.type, "disk_octets", sizeof(vl.type));
+    vl.values[0].derive = ps->io_diskr;
+    vl.values[1].derive = ps->io_diskw;
+    vl.values_len = 2;
+    plugin_dispatch_values(&vl);
+  }
+
+  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, "file_handles", sizeof(vl.type));
+    sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
+    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));
     vl.values[0].derive = ps->cswitch_vol;
@@ -709,19 +804,23 @@ static void ps_submit_proc_list(procstat_t *ps) {
     plugin_dispatch_values(&vl);
   }
 
-  DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; "
-        "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
-        "vmem_code = %lu; "
-        "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
-        "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
-        "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
-        "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
-        "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
-        ps->name, ps->num_proc, ps->num_lwp, 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->cswitch_vol,
-        ps->cswitch_invol);
+  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 "; "
+      "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
+      "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
+      "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->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
@@ -741,7 +840,7 @@ static void ps_submit_fork_rate(derive_t value) {
 
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
-static int ps_read_tasks_status(procstat_entry_t *ps) {
+static int ps_read_tasks_status(process_entry_t *ps) {
   char dirname[64];
   DIR *dh;
   char filename[64];
@@ -753,11 +852,11 @@ static int ps_read_tasks_status(procstat_entry_t *ps) {
   char *fields[8];
   int numfields;
 
-  ssnprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
+  snprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
 
   if ((dh = opendir(dirname)) == NULL) {
     DEBUG("Failed to open directory `%s'", dirname);
-    return (-1);
+    return -1;
   }
 
   while ((ent = readdir(dh)) != NULL) {
@@ -768,8 +867,12 @@ static int ps_read_tasks_status(procstat_entry_t *ps) {
 
     tpid = ent->d_name;
 
-    ssnprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
-              tpid);
+    if (snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
+                 tpid) >= sizeof(filename)) {
+      DEBUG("Filename too long: `%s'", filename);
+      continue;
+    }
+
     if ((fh = fopen(filename, "r")) == NULL) {
       DEBUG("Failed to open file `%s'", filename);
       continue;
@@ -811,11 +914,11 @@ static int ps_read_tasks_status(procstat_entry_t *ps) {
   ps->cswitch_vol = cswitch_vol;
   ps->cswitch_invol = cswitch_invol;
 
-  return (0);
+  return 0;
 } /* int *ps_read_tasks_status */
 
 /* Read data from /proc/pid/status */
-static procstat_t *ps_read_status(long pid, procstat_t *ps) {
+static int ps_read_status(long pid, process_entry_t *ps) {
   FILE *fh;
   char buffer[1024];
   char filename[64];
@@ -826,9 +929,9 @@ static procstat_t *ps_read_status(long pid, procstat_t *ps) {
   char *fields[8];
   int numfields;
 
-  ssnprintf(filename, sizeof(filename), "/proc/%li/status", pid);
+  snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
   if ((fh = fopen(filename, "r")) == NULL)
-    return (NULL);
+    return -1;
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     unsigned long tmp;
@@ -868,10 +971,10 @@ static procstat_t *ps_read_status(long pid, procstat_t *ps) {
   if (threads != 0)
     ps->num_lwp = threads;
 
-  return (ps);
-} /* procstat_t *ps_read_vmem */
+  return 0;
+} /* int *ps_read_status */
 
-static int ps_read_io(procstat_entry_t *ps) {
+static int ps_read_io(process_entry_t *ps) {
   FILE *fh;
   char buffer[1024];
   char filename[64];
@@ -879,9 +982,11 @@ static int ps_read_io(procstat_entry_t *ps) {
   char *fields[8];
   int numfields;
 
-  ssnprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
-  if ((fh = fopen(filename, "r")) == NULL)
-    return (-1);
+  snprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
+  if ((fh = fopen(filename, "r")) == NULL) {
+    DEBUG("ps_read_io: Failed to open file `%s'", filename);
+    return -1;
+  }
 
   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
     derive_t *val = NULL;
@@ -896,6 +1001,10 @@ static int ps_read_io(procstat_entry_t *ps) {
       val = &(ps->io_syscr);
     else if (strncasecmp(buffer, "syscw:", 6) == 0)
       val = &(ps->io_syscw);
+    else if (strncasecmp(buffer, "read_bytes:", 11) == 0)
+      val = &(ps->io_diskr);
+    else if (strncasecmp(buffer, "write_bytes:", 12) == 0)
+      val = &(ps->io_diskw);
     else
       continue;
 
@@ -917,35 +1026,88 @@ static int ps_read_io(procstat_entry_t *ps) {
     char errbuf[1024];
     WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
   }
-  return (0);
+  return 0;
 } /* int ps_read_io (...) */
 
-static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry) {
-  if (entry->has_io == 0 && ps_read_io(entry) != 0) {
-    /* no io data */
-    entry->io_rchar = -1;
-    entry->io_wchar = -1;
-    entry->io_syscr = -1;
-    entry->io_syscw = -1;
+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;
+  struct dirent *ent;
+  int count = 0;
+
+  snprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
+
+  if ((dh = opendir(dirname)) == NULL) {
+    DEBUG("Failed to open directory `%s'", dirname);
+    return -1;
+  }
+  while ((ent = readdir(dh)) != NULL) {
+    if (!isdigit((int)ent->d_name[0]))
+      continue;
+    else
+      count++;
+  }
+  closedir(dh);
 
-    DEBUG("ps_read_process: not get io data for pid %li", entry->id);
+  return (count >= 1) ? count : 1;
+} /* int ps_count_fd (pid) */
+
+static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
+  if (entry->has_io == 0) {
+    ps_read_io(entry);
+    entry->has_io = 1;
   }
-  entry->has_io = 1;
 
-  if (report_ctx_switch) {
-    if (entry->has_cswitch == 0 && ps_read_tasks_status(entry) != 0) {
-      entry->cswitch_vol = -1;
-      entry->cswitch_invol = -1;
+  if (ps->report_ctx_switch) {
+    if (entry->has_cswitch == 0) {
+      ps_read_tasks_status(entry);
+      entry->has_cswitch = 1;
+    }
+  }
 
-      DEBUG("ps_read_tasks_status: not get context "
-            "switch data for pid %li",
-            entry->id);
+  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_cswitch = 1;
+    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) {
+      entry->num_fd = num_fd;
+    }
+    entry->has_fd = 1;
   }
 } /* void ps_fill_details (...) */
 
-static int ps_read_process(long pid, procstat_t *ps, char *state) {
+static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   char filename[64];
   char buffer[1024];
 
@@ -967,13 +1129,11 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
 
   ssize_t status;
 
-  memset(ps, 0, sizeof(procstat_t));
-
-  ssnprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
+  snprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
 
   status = read_file_contents(filename, buffer, sizeof(buffer) - 1);
   if (status <= 0)
-    return (-1);
+    return -1;
   buffer_len = (size_t)status;
   buffer[buffer_len] = 0;
 
@@ -995,7 +1155,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   if (name_start_pos >= name_end_pos) {
     ERROR("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
           name_start_pos, name_end_pos);
-    return (-1);
+    return -1;
   }
 
   name_len = (name_end_pos - name_start_pos) - 1;
@@ -1005,7 +1165,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
 
   if ((buffer_len - name_end_pos) < 2)
-    return (-1);
+    return -1;
   buffer_ptr = &buffer[name_end_pos + 2];
 
   fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
@@ -1013,7 +1173,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
     DEBUG("processes plugin: ps_read_process (pid = %li):"
           " `%s' has only %i fields..",
           pid, filename, fields_len);
-    return (-1);
+    return -1;
   }
 
   *state = fields[0][0];
@@ -1023,7 +1183,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
     ps->num_proc = 0;
   } else {
     ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
-    if ((ps_read_status(pid, ps)) == NULL) {
+    if ((ps_read_status(pid, ps)) != 0) {
       /* No VMem data */
       ps->vmem_data = -1;
       ps->vmem_code = -1;
@@ -1039,7 +1199,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
     DEBUG("processes plugin: This is only a zombie: pid = %li; "
           "name = %s;",
           pid, ps->name);
-    return (0);
+    return 0;
   }
 
   cpu_user_counter = atoll(fields[11]);
@@ -1068,8 +1228,19 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   ps->vmem_rss = (unsigned long)vmem_rss;
   ps->stack_size = (unsigned long)stack_size;
 
+  /* no data by default. May be filled by ps_fill_details () */
+  ps->io_rchar = -1;
+  ps->io_wchar = -1;
+  ps->io_syscr = -1;
+  ps->io_syscw = -1;
+  ps->io_diskr = -1;
+  ps->io_diskw = -1;
+
+  ps->cswitch_vol = -1;
+  ps->cswitch_invol = -1;
+
   /* success */
-  return (0);
+  return 0;
 } /* int ps_read_process (...) */
 
 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
@@ -1084,7 +1255,7 @@ static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
   if ((pid < 1) || (NULL == buf) || (buf_len < 2))
     return NULL;
 
-  ssnprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
+  snprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
 
   errno = 0;
   fd = open(file, O_RDONLY);
@@ -1139,7 +1310,7 @@ static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
     if (NULL == name)
       return NULL;
 
-    ssnprintf(buf, buf_len, "[%s]", name);
+    snprintf(buf, buf_len, "[%s]", name);
     return buf;
   }
 
@@ -1176,7 +1347,7 @@ static int read_fork_rate(void) {
     char errbuf[1024];
     ERROR("processes plugin: fopen (/proc/stat) failed: %s",
           sstrerror(errno, errbuf, sizeof(errbuf)));
-    return (-1);
+    return -1;
   }
 
   while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
@@ -1200,10 +1371,10 @@ static int read_fork_rate(void) {
   fclose(proc_stat);
 
   if (!value_valid)
-    return (-1);
+    return -1;
 
   ps_submit_fork_rate(value.derive);
-  return (0);
+  return 0;
 }
 #endif /*KERNEL_LINUX */
 
@@ -1223,13 +1394,13 @@ static char *ps_get_cmdline(long pid,
           "while reading \"%s\": "
           "Returned %zd but expected %zu.",
           path, status, buffer_size);
-    return (NULL);
+    return NULL;
   }
 
   info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
   sstrncpy(buffer, info.pr_psargs, buffer_size);
 
-  return (buffer);
+  return buffer;
 } /* }}} int ps_get_cmdline */
 
 /*
@@ -1239,7 +1410,7 @@ static char *ps_get_cmdline(long pid,
  * The values for input and ouput chars are calculated "by hand"
  * Added a few "solaris" specific process states as well
  */
-static int ps_read_process(long pid, procstat_t *ps, char *state) {
+static int ps_read_process(long pid, process_entry_t *ps, char *state) {
   char filename[64];
   char f_psinfo[64], f_usage[64];
   char *buffer;
@@ -1274,7 +1445,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
     sfree(myStatus);
     sfree(myInfo);
     sfree(myUsage);
-    return (0);
+    return 0;
   } else {
     ps->num_proc = 1;
     ps->num_lwp = myInfo->pr_nlwp;
@@ -1304,6 +1475,14 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   ps->stack_size = myStatus->pr_stksize;
 
   /*
+   * TODO: File descriptor count for Solaris
+   */
+  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
    * then convert input/output blocks to chars
@@ -1317,6 +1496,8 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   ps->io_wchar = myUsage->pr_oublk * chars_per_block;
   ps->io_syscr = myUsage->pr_sysc;
   ps->io_syscw = myUsage->pr_sysc;
+  ps->io_diskr = -1;
+  ps->io_diskw = -1;
 
   /*
    * TODO: context switch counters for Solaris
@@ -1346,7 +1527,7 @@ static int ps_read_process(long pid, procstat_t *ps, char *state) {
   sfree(myInfo);
   sfree(myUsage);
 
-  return (0);
+  return 0;
 }
 
 /*
@@ -1359,7 +1540,7 @@ static int read_fork_rate(void) {
   derive_t result = 0;
 
   if (kc == NULL)
-    return (-1);
+    return -1;
 
   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
        ksp_chain = ksp_chain->ks_next) {
@@ -1377,7 +1558,7 @@ static int read_fork_rate(void) {
   }
 
   ps_submit_fork_rate(result);
-  return (0);
+  return 0;
 }
 #endif /* KERNEL_SOLARIS */
 
@@ -1394,12 +1575,12 @@ static int mach_get_task_name(task_t t, int *pid, char *name,
   mib[2] = KERN_PROC_PID;
 
   if (pid_for_task(t, pid) != KERN_SUCCESS)
-    return (-1);
+    return -1;
   mib[3] = *pid;
 
   kp_size = sizeof(kp);
   if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
-    return (-1);
+    return -1;
 
   if (name_max_len > (MAXCOMLEN + 1))
     name_max_len = MAXCOMLEN + 1;
@@ -1413,7 +1594,7 @@ static int mach_get_task_name(task_t t, int *pid, char *name,
    * `top' does it, because it is a lot of work and only used when
    * debugging. -octo */
 
-  return (0);
+  return 0;
 }
 #endif /* HAVE_THREAD_INFO */
 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
@@ -1444,7 +1625,7 @@ static int ps_read(void) {
   int blocked = 0;
 
   procstat_t *ps;
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
@@ -1526,6 +1707,19 @@ static int ps_read(void) {
         pse.vmem_data = 0;
         pse.vmem_code = 0;
 
+        pse.io_rchar = -1;
+        pse.io_wchar = -1;
+        pse.io_syscr = -1;
+        pse.io_syscw = -1;
+        pse.io_diskr = -1;
+        pse.io_diskw = -1;
+
+        /* 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;
 
@@ -1657,8 +1851,7 @@ static int ps_read(void) {
   char cmdline[CMDLINE_BUFFER_SIZE];
 
   int status;
-  procstat_t ps;
-  procstat_entry_t pse;
+  process_entry_t pse;
   char state;
 
   running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1667,7 +1860,7 @@ static int ps_read(void) {
   if ((proc = opendir("/proc")) == NULL) {
     char errbuf[1024];
     ERROR("Cannot open `/proc': %s", sstrerror(errno, errbuf, sizeof(errbuf)));
-    return (-1);
+    return -1;
   }
 
   while ((ent = readdir(proc)) != NULL) {
@@ -1677,38 +1870,15 @@ static int ps_read(void) {
     if ((pid = atol(ent->d_name)) < 1)
       continue;
 
-    status = ps_read_process(pid, &ps, &state);
+    memset(&pse, 0, sizeof(pse));
+    pse.id = pid;
+
+    status = ps_read_process(pid, &pse, &state);
     if (status != 0) {
       DEBUG("ps_read_process failed: %i", status);
       continue;
     }
 
-    memset(&pse, 0, sizeof(pse));
-    pse.id = pid;
-    pse.age = 0;
-
-    pse.num_proc = ps.num_proc;
-    pse.num_lwp = ps.num_lwp;
-    pse.vmem_size = ps.vmem_size;
-    pse.vmem_rss = ps.vmem_rss;
-    pse.vmem_data = ps.vmem_data;
-    pse.vmem_code = ps.vmem_code;
-    pse.stack_size = ps.stack_size;
-
-    pse.vmem_minflt_counter = ps.vmem_minflt_counter;
-    pse.vmem_majflt_counter = ps.vmem_majflt_counter;
-
-    pse.cpu_user_counter = ps.cpu_user_counter;
-    pse.cpu_system_counter = ps.cpu_system_counter;
-
-    pse.io_rchar = ps.io_rchar;
-    pse.io_wchar = ps.io_wchar;
-    pse.io_syscr = ps.io_syscr;
-    pse.io_syscw = ps.io_syscw;
-
-    pse.cswitch_vol = ps.cswitch_vol;
-    pse.cswitch_invol = ps.cswitch_invol;
-
     switch (state) {
     case 'R':
       running++;
@@ -1730,8 +1900,8 @@ static int ps_read(void) {
       break;
     }
 
-    ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
-                &pse);
+    ps_list_add(pse.name,
+                ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
   }
 
   closedir(proc);
@@ -1764,7 +1934,7 @@ static int ps_read(void) {
   struct kinfo_proc *proc_ptr = NULL;
   int count; /* returns number of processes */
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
@@ -1772,7 +1942,7 @@ static int ps_read(void) {
   kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
   if (kd == NULL) {
     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
-    return (0);
+    return 0;
   }
 
   /* Get the list of processes. */
@@ -1781,7 +1951,7 @@ static int ps_read(void) {
     ERROR("processes plugin: Cannot get kvm processes list: %s",
           kvm_geterr(kd));
     kvm_close(kd);
-    return (0);
+    return 0;
   }
 
   /* Iterate through the processes in kinfo_proc */
@@ -1814,8 +1984,8 @@ static int ps_read(void) {
         }
       } /* if (process has argument list) */
 
+      memset(&pse, 0, sizeof(pse));
       pse.id = procs[i].ki_pid;
-      pse.age = 0;
 
       pse.num_proc = 1;
       pse.num_lwp = procs[i].ki_numthreads;
@@ -1849,6 +2019,14 @@ static int ps_read(void) {
       pse.io_wchar = -1;
       pse.io_syscr = -1;
       pse.io_syscw = -1;
+      pse.io_diskr = -1;
+      pse.io_diskw = -1;
+
+      /* 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;
@@ -1911,15 +2089,15 @@ static int ps_read(void) {
   struct kinfo_proc *proc_ptr = NULL;
   int count; /* returns number of processes */
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
 
   /* Open the kvm interface, get a descriptor */
-  kd = kvm_open(NULL, NULL, NULL, 0, errbuf);
+  kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
   if (kd == NULL) {
     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
-    return (0);
+    return 0;
   }
 
   /* Get the list of processes. */
@@ -1928,7 +2106,7 @@ static int ps_read(void) {
     ERROR("processes plugin: Cannot get kvm processes list: %s",
           kvm_geterr(kd));
     kvm_close(kd);
-    return (0);
+    return 0;
   }
 
   /* Iterate through the processes in kinfo_proc */
@@ -1963,7 +2141,6 @@ static int ps_read(void) {
 
       memset(&pse, 0, sizeof(pse));
       pse.id = procs[i].p_pid;
-      pse.age = 0;
 
       pse.num_proc = 1;
       pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
@@ -1986,6 +2163,14 @@ static int ps_read(void) {
       pse.io_wchar = -1;
       pse.io_syscr = -1;
       pse.io_syscw = -1;
+      pse.io_diskr = -1;
+      pse.io_diskw = -1;
+
+      /* 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;
@@ -2045,7 +2230,7 @@ static int ps_read(void) {
   pid_t pindex = 0;
   int nprocs;
 
-  procstat_entry_t pse;
+  process_entry_t pse;
 
   ps_list_reset();
   while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
@@ -2085,8 +2270,9 @@ static int ps_read(void) {
         }
       }
 
+      memset(&pse, 0, sizeof(pse));
+
       pse.id = procentry[i].pi_pid;
-      pse.age = 0;
       pse.num_lwp = procentry[i].pi_thcount;
       pse.num_proc = 1;
 
@@ -2136,7 +2322,7 @@ static int ps_read(void) {
 
       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
-      /* Not supported */
+      /* Not supported/implemented */
       pse.vmem_data = 0;
       pse.vmem_code = 0;
       pse.stack_size = 0;
@@ -2145,6 +2331,11 @@ static int ps_read(void) {
       pse.io_wchar = -1;
       pse.io_syscr = -1;
       pse.io_syscw = -1;
+      pse.io_diskr = -1;
+      pse.io_diskw = -1;
+
+      pse.num_fd = 0;
+      pse.num_maps = 0;
 
       pse.cswitch_vol = -1;
       pse.cswitch_invol = -1;
@@ -2194,12 +2385,11 @@ static int ps_read(void) {
 
   proc = opendir("/proc");
   if (proc == NULL)
-    return (-1);
+    return -1;
 
   while ((ent = readdir(proc)) != NULL) {
     long pid;
-    struct procstat ps;
-    procstat_entry_t pse;
+    process_entry_t pse;
     char *endptr;
 
     if (!isdigit((int)ent->d_name[0]))
@@ -2209,38 +2399,15 @@ static int ps_read(void) {
     if (*endptr != 0) /* value didn't completely parse as a number */
       continue;
 
-    status = ps_read_process(pid, &ps, &state);
+    memset(&pse, 0, sizeof(pse));
+    pse.id = pid;
+
+    status = ps_read_process(pid, &pse, &state);
     if (status != 0) {
       DEBUG("ps_read_process failed: %i", status);
       continue;
     }
 
-    memset(&pse, 0, sizeof(pse));
-    pse.id = pid;
-    pse.age = 0;
-
-    pse.num_proc = ps.num_proc;
-    pse.num_lwp = ps.num_lwp;
-    pse.vmem_size = ps.vmem_size;
-    pse.vmem_rss = ps.vmem_rss;
-    pse.vmem_data = ps.vmem_data;
-    pse.vmem_code = ps.vmem_code;
-    pse.stack_size = ps.stack_size;
-
-    pse.vmem_minflt_counter = ps.vmem_minflt_counter;
-    pse.vmem_majflt_counter = ps.vmem_majflt_counter;
-
-    pse.cpu_user_counter = ps.cpu_user_counter;
-    pse.cpu_system_counter = ps.cpu_system_counter;
-
-    pse.io_rchar = ps.io_rchar;
-    pse.io_wchar = ps.io_wchar;
-    pse.io_syscr = ps.io_syscr;
-    pse.io_syscw = ps.io_syscw;
-
-    pse.cswitch_vol = -1;
-    pse.cswitch_invol = -1;
-
     switch (state) {
     case 'R':
       running++;
@@ -2268,8 +2435,8 @@ static int ps_read(void) {
       break;
     }
 
-    ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
-                &pse);
+    ps_list_add(pse.name,
+                ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
   } /* while(readdir) */
   closedir(proc);
 
@@ -2290,7 +2457,7 @@ static int ps_read(void) {
 
   want_init = 0;
 
-  return (0);
+  return 0;
 } /* int ps_read */
 
 void module_register(void) {