From 77311aed583ac4566d76f1ce3a1dc9deae2e59dc Mon Sep 17 00:00:00 2001 From: Cosmin Ioiart Date: Tue, 6 Mar 2012 15:58:25 +0100 Subject: [PATCH] Added support for the Solaris OS processes 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 | 304 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 296 insertions(+), 8 deletions(-) diff --git a/src/processes.c b/src/processes.c index 66237dc3..61955f82 100644 --- a/src/processes.c +++ b/src/processes.c @@ -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 * Manuel Sanmartin * Clément Stenac + * Cosmin Ioiart **/ #include "collectd.h" @@ -109,6 +111,11 @@ #define MAXARGLN 1024 /* #endif HAVE_PROCINFO_H */ +#elif KERNEL_SOLARIS +# include +# include +/* #endif KERNEL_SOLARIS */ + #else # error "No applicable input method." #endif @@ -117,6 +124,10 @@ # include #endif +#if HAVE_KSTAT_H +# include +#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 */ -- 2.30.2