* 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
* 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
return result;
}
+#endif /*KERNEL_LINUX */
+#if KERNEL_LINUX || KERNEL_SOLARIS
static void ps_submit_fork_rate (unsigned long value)
{
value_t values[1];
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)
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 */