Added support for the Solaris OS processes
authorCosmin Ioiart <cioiart+dev@gmail.com>
Tue, 6 Mar 2012 14:58:25 +0000 (15:58 +0100)
committerFlorian Forster <octo@collectd.org>
Sun, 23 Sep 2012 10:39:31 +0000 (12:39 +0200)
  Added support for process information on the Solaris OS. The information comes mainly from
  /proc/PID/status, /proc/PID/psinfo , /proc/PID/usage and kstat
  Added a few "solaris" specific process states as well

src/processes.c

index 66237dc..61955f8 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2009       Andrés J. Díaz
  * Copyright (C) 2009       Manuel Sanmartin
  * Copyright (C) 2010       Clément Stenac
+ * Copyright (C) 2012       Cosmin Ioiart
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -30,6 +31,7 @@
  *   Andrés J. Díaz <ajdiaz at connectical.com>
  *   Manuel Sanmartin
  *   Clément Stenac <clement.stenac at diwi.org>
+ *   Cosmin Ioiart <cioiart at gmail.com>
  **/
 
 #include "collectd.h"
 #define MAXARGLN 1024
 /* #endif HAVE_PROCINFO_H */
 
+#elif KERNEL_SOLARIS
+# include <procfs.h>
+# include <dirent.h>
+/* #endif KERNEL_SOLARIS */
+
 #else
 # error "No applicable input method."
 #endif
 # include <regex.h>
 #endif
 
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+
 #ifndef ARG_MAX
 #  define ARG_MAX 4096
 #endif
@@ -1161,7 +1172,9 @@ static unsigned long read_fork_rate ()
 
        return result;
 }
+#endif /*KERNEL_LINUX */
 
+#if KERNEL_LINUX || KERNEL_SOLARIS
 static void ps_submit_fork_rate (unsigned long value)
 {
        value_t values[1];
@@ -1171,16 +1184,175 @@ static void ps_submit_fork_rate (unsigned long value)
 
        vl.values = values;
        vl.values_len = 1;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
-       sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+       sstrncpy(vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy(vl.plugin, "processes", sizeof (vl.plugin));
+       sstrncpy(vl.plugin_instance, "", sizeof (vl.plugin_instance));
+       sstrncpy(vl.type, "fork_rate", sizeof (vl.type));
+       sstrncpy(vl.type_instance, "", sizeof (vl.type_instance));
 
-       plugin_dispatch_values (&vl);
+       plugin_dispatch_values(&vl);
+}
+#endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
+
+#if KERNEL_SOLARIS
+static char *ps_get_cmdline(pid_t pid)
+{
+       char f_psinfo[64];
+       char *buffer = NULL;
+       psinfo_t *myInfo;
+
+       snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid);
+
+       buffer = malloc(sizeof (psinfo_t));
+       read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
+       buffer[sizeof (psinfo_t)] = 0;
+       myInfo = (psinfo_t *) buffer;
+
+       sstrncpy(buffer, myInfo->pr_psargs, sizeof (myInfo->pr_psargs));
+
+       free(myInfo);
+       return strtok(buffer, " ");
 }
 
-#endif /* KERNEL_LINUX */
+/*
+ * Reads process information on the Solaris OS. The information comes mainly from
+ * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
+ * 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(int pid, procstat_t *ps, char *state)
+{
+
+       char filename[64];
+       char f_psinfo[64], f_usage[64];
+       int i;
+       char *buffer;
+
+
+       pstatus_t *myStatus;
+       psinfo_t *myInfo;
+       prusage_t *myUsage;
+
+       snprintf(filename, sizeof (filename), "/proc/%i/status", pid);
+       snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid);
+       snprintf(f_usage, sizeof (f_usage), "/proc/%i/usage", pid);
+
+
+       buffer = malloc(sizeof (pstatus_t));
+       read_file_contents(filename, buffer, sizeof (pstatus_t));
+       myStatus = (pstatus_t *) buffer;
+
+       buffer = malloc(sizeof (psinfo_t));
+       read_file_contents(f_psinfo, buffer, sizeof (psinfo_t));
+       buffer[sizeof (psinfo_t)] = 0;
+       myInfo = (psinfo_t *) buffer;
+
+       buffer = malloc(sizeof (prusage_t));
+       read_file_contents(f_usage, buffer, sizeof (prusage_t));
+       myUsage = (prusage_t *) buffer;
+
+       sstrncpy(ps->name, myInfo->pr_fname, sizeof (myInfo->pr_fname));
+       ps->num_lwp = myStatus->pr_nlwp;
+       if (myInfo->pr_wstat != 0) {
+               ps->num_proc = 0;
+               ps->num_lwp = 0;
+               *state = (char) 'Z';
+               return (0);
+       } else {
+               ps->num_proc = 1;
+               ps->num_lwp = myInfo->pr_nlwp;
+       }
+
+       /*
+        * Convert system time and user time from nanoseconds to microseconds
+        * for compatibility with the linux module
+        */
+       ps->cpu_system_counter = myStatus -> pr_stime.tv_nsec / 1000;
+       ps->cpu_user_counter = myStatus -> pr_utime.tv_nsec / 1000;
+
+       /*
+        * Convert rssize from KB to bytes to be consistent w/ the linux module
+        */
+       ps->vmem_rss = myInfo->pr_rssize * 1024;
+       ps->vmem_size = myInfo->pr_size * 1024;
+       ps->vmem_minflt_counter = myUsage->pr_minf;
+       ps->vmem_majflt_counter = myUsage->pr_majf;
+
+       /*
+        * TODO: Data and code segment calculations for Solaris
+        */
+
+       ps->vmem_data = -1;
+       ps->vmem_code = -1;
+       ps->stack_size = myStatus->pr_stksize;
+
+       /*
+        * Calculating input/ouput chars
+        * Formula used is total chars / total blocks => chars/block
+        * then convert input/output blocks to chars
+        */
+       ulong_t tot_chars = myUsage->pr_ioch;
+       ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
+       ulong_t chars_per_block = 1;
+       if (tot_blocks != 0)
+               chars_per_block = tot_chars / tot_blocks;
+       ps->io_rchar = myUsage->pr_inblk * chars_per_block;
+       ps->io_wchar = myUsage->pr_oublk * chars_per_block;
+       ps->io_syscr = myUsage->pr_sysc;
+       ps->io_syscw = myUsage->pr_sysc;
+
+
+       /*
+        * TODO: Find way of setting BLOCKED and PAGING status
+        */
+
+       *state = (char) 'R';
+       if (myStatus->pr_flags & PR_ASLEEP)
+               *state = (char) 'S';
+       else if (myStatus->pr_flags & PR_STOPPED)
+               *state = (char) 'T';
+       else if (myStatus->pr_flags & PR_DETACH)
+               *state = (char) 'E';
+       else if (myStatus->pr_flags & PR_DAEMON)
+               *state = (char) 'A';
+       else if (myStatus->pr_flags & PR_ISSYS)
+               *state = (char) 'Y';
+       else if (myStatus->pr_flags & PR_ORPHAN)
+               *state = (char) 'O';
+
+       free(myStatus);
+       free(myInfo);
+       free(myUsage);
+
+       return (0);
+}
+
+/*
+ * Reads the number of threads created since the last reboot. On Solaris these
+ * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
+ * The result is the sum for all the threads created on each cpu
+ */
+static unsigned long read_fork_rate()
+{
+       extern kstat_ctl_t *kc;
+       kstat_t *ksp_chain = NULL;
+       unsigned long result = 0;
+
+       if (kc == NULL)
+               return ULONG_MAX;
+
+       for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
+            ksp_chain = ksp_chain->ks_next) {
+               if ((strncmp(ksp_chain->ks_module, "cpu", 3) == 0) &&
+                   (strncmp(ksp_chain->ks_name, "sys", 3) == 0) &&
+                   (strncmp(ksp_chain->ks_class, "misc", 4) == 0)) {
+                       kstat_read(kc, ksp_chain, NULL);
+                       result += get_kstat_value(ksp_chain, "nthreads");
+               }
+       }
+       return result;
+}
+#endif /* KERNEL_SOLARIS */
 
 #if HAVE_THREAD_INFO
 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
@@ -1870,7 +2042,123 @@ static int ps_read (void)
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
                ps_submit_proc_list (ps);
-#endif /* HAVE_PROCINFO_H */
+/* #endif HAVE_PROCINFO_H */
+
+#elif KERNEL_SOLARIS
+       /*
+         * The Solaris section adds a few more process states and removes some
+         * process states compared to linux. Most notably there is no "PAGING"
+         * and "BLOCKED" state for a process.  The rest is similar to the linux
+         * code.
+        */
+       int running = 0;
+       int sleeping = 0;
+       int zombies = 0;
+       int stopped = 0;
+       int detached = 0;
+       int daemon = 0;
+       int system = 0;
+       int orphan = 0;
+       unsigned long fork_rate;
+       struct dirent *ent;
+       DIR *proc;
+       int pid;
+
+       int status;
+       struct procstat ps;
+       procstat_entry_t pse;
+       procstat_t *ps_ptr;
+       char state;
+
+       ps_list_reset();
+
+
+       proc = opendir("/proc");
+       if (proc == NULL) {
+               return (-1);
+       }
+
+       while ((ent = readdir(proc)) != NULL) {
+               if (!isdigit(ent->d_name[0]))
+                       continue;
+
+               if ((pid = atoi(ent->d_name)) < 1)
+                       continue;
+
+               status = ps_read_process(pid, &ps, &state);
+               if (status != 0) {
+                       DEBUG("ps_read_process failed: %i", status);
+                       continue;
+               }
+               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 = 0;
+               pse.vmem_minflt_counter = ps.vmem_minflt_counter;
+               pse.vmem_majflt = 0;
+               pse.vmem_majflt_counter = ps.vmem_majflt_counter;
+
+               pse.cpu_user = 0;
+               pse.cpu_user_counter = ps.cpu_user_counter;
+               pse.cpu_system = 0;
+               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;
+
+               switch (state) {
+               case 'R': running++;
+                       break;
+               case 'S': sleeping++;
+                       break;
+               case 'E': detached++;
+                       break;
+               case 'Z': zombies++;
+                       break;
+               case 'T': stopped++;
+                       break;
+               case 'A': daemon++;
+                       break;
+               case 'Y': system++;
+                       break;
+               case 'O': orphan++;
+                       break;
+               }
+
+               ps_list_add(ps.name, ps_get_cmdline(pid), &pse);
+
+       } // while()
+       closedir(proc);
+
+       ps_submit_state("running", running);
+       ps_submit_state("sleeping", sleeping);
+       ps_submit_state("zombies", zombies);
+       ps_submit_state("stopped", stopped);
+       ps_submit_state("detached", detached);
+       ps_submit_state("daemon", daemon);
+       ps_submit_state("system", system);
+       ps_submit_state("orphan", orphan);
+
+       for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
+               ps_submit_proc_list(ps_ptr);
+
+
+       fork_rate = read_fork_rate();
+       if (fork_rate != ULONG_MAX) {
+               ps_submit_fork_rate(fork_rate);
+       }
+
+#endif /* KERNEL_SOLARIS */
 
        return (0);
 } /* int ps_read */