From e61f047ef656c49700ff49a4854bb2da4bec1b9d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Manuel=20Luis=20Sanmart=C3=ADn=20Rozada?= Date: Wed, 20 May 2015 22:37:41 +0200 Subject: [PATCH] Plugin process: collect voluntary and involuntary context switches. --- src/collectd.conf.pod | 4 ++ src/processes.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/types.db | 2 + 3 files changed, 149 insertions(+), 3 deletions(-) diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 0c8e1e20..1884914f 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -5422,6 +5422,10 @@ dispatched to the daemon using the specified I as an identifier. This allows to "group" several processes together. I must not contain slashes. +=item B I + +Collect context switch of the process. + =back =head2 Plugin C diff --git a/src/processes.c b/src/processes.c index 0649eab2..c1f92a7d 100644 --- a/src/processes.c +++ b/src/processes.c @@ -180,6 +180,9 @@ typedef struct procstat_entry_s derive_t io_syscr; derive_t io_syscw; + derive_t cswitch_vol; + derive_t cswitch_invol; + struct procstat_entry_s *next; } procstat_entry_t; @@ -211,12 +214,17 @@ typedef struct procstat derive_t io_syscr; derive_t io_syscw; + derive_t cswitch_vol; + derive_t cswitch_invol; + struct procstat *next; struct procstat_entry_s *instances; } procstat_t; static procstat_t *list_head_g = NULL; +static _Bool ps_ctx_switch = 0; + #if HAVE_THREAD_INFO static mach_port_t port_host_self; static mach_port_t port_task_self; @@ -354,7 +362,7 @@ static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps) static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry) { procstat_t *ps; - procstat_entry_t *pse; + procstat_entry_t *pse; if (entry->id == 0) return; @@ -398,6 +406,8 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t 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; @@ -412,6 +422,9 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t 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); + if ((entry->vmem_minflt_counter == 0) && (entry->vmem_majflt_counter == 0)) { @@ -485,6 +498,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t ps->cpu_user_counter += pse->cpu_user; ps->cpu_system_counter += pse->cpu_system; + } } @@ -508,6 +522,8 @@ static void ps_list_reset (void) 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; @@ -592,6 +608,17 @@ static int ps_config (oconfig_item_t *ci) ps_list_register (c->values[0].value.string, c->values[1].value.string); } + else if (strcasecmp (c->key, "CollectContextSwitch") == 0) + { + if ((c->values_num != 1) + || (c->values[0].type != OCONFIG_TYPE_BOOLEAN)) + { + ERROR ("processes plugin: `CollectContextSwitch' needs exactly " + "one boolean argument."); + continue; + } + ps_ctx_switch = c->values[0].value.boolean ? 1 : 0; + } else { ERROR ("processes plugin: The `%s' configuration option is not " @@ -741,19 +768,34 @@ static void ps_submit_proc_list (procstat_t *ps) plugin_dispatch_values (&vl); } + if ( ps_ctx_switch ) + { + sstrncpy (vl.type, "ps_cswitch_vol", sizeof (vl.type)); + vl.values[0].derive = ps->cswitch_vol; + vl.values_len = 1; + plugin_dispatch_values (&vl); + + sstrncpy (vl.type, "ps_cswitch_invol", sizeof (vl.type)); + vl.values[0].derive = ps->cswitch_invol; + vl.values_len = 1; + 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";", + "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->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, + ps->cswitch_vol, ps->cswitch_invol); } /* void ps_submit_proc_list */ #if KERNEL_LINUX || KERNEL_SOLARIS @@ -778,6 +820,89 @@ static void ps_submit_fork_rate (derive_t value) /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */ #if KERNEL_LINUX +static procstat_t *ps_read_tasks_status (int pid, procstat_t *ps) +{ + char dirname[64]; + DIR *dh; + char filename[64]; + FILE *fh; + struct dirent *ent; + unsigned long long cswitch_vol = 0; + unsigned long long cswitch_invol = 0; + char buffer[1024]; + char *fields[8]; + int numfields; + + ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid); + + if ((dh = opendir (dirname)) == NULL) + { + DEBUG ("Failed to open directory `%s'", dirname); + return (NULL); + } + + while ((ent = readdir (dh)) != NULL) + { + char *tpid; + + if (!isdigit ((int) ent->d_name[0])) + continue; + + tpid = ent->d_name; + + ssnprintf (filename, sizeof (filename), "/proc/%i/task/%s/status", pid, tpid); + if ((fh = fopen (filename, "r")) == NULL) + { + DEBUG ("Failed to open file `%s'", filename); + continue; + } + + while (fgets (buffer, sizeof(buffer), fh) != NULL) + { + long long tmp; + char *endptr; + + if (strncmp (buffer, "voluntary_ctxt_switches", 23) != 0 + && strncmp (buffer, "nonvoluntary_ctxt_switches", 26) != 0) + continue; + + numfields = strsplit (buffer, fields, + STATIC_ARRAY_SIZE (fields)); + + if (numfields < 2) + continue; + + errno = 0; + endptr = NULL; + tmp = strtoll (fields[1], &endptr, /* base = */ 10); + if ((errno == 0) && (endptr != fields[1])) + { + if (strncmp (buffer, "voluntary_ctxt_switches", 23) == 0) + { + cswitch_vol += tmp; + } + else if (strncmp (buffer, "nonvoluntary_ctxt_switches", 26) == 0) + { + cswitch_invol += tmp; + } + } + } /* while (fgets) */ + + if (fclose (fh)) + { + char errbuf[1024]; + WARNING ("processes: fclose: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + } + } + closedir (dh); + + ps->cswitch_vol = cswitch_vol; + ps->cswitch_invol = cswitch_invol; + + return (ps); +} /* int *ps_read_tasks_status */ + static int ps_read_tasks (int pid) { char dirname[64]; @@ -1068,6 +1193,18 @@ int ps_read_process (int pid, procstat_t *ps, char *state) DEBUG("ps_read_process: not get io data for pid %i",pid); } + if ( ps_ctx_switch ) + { + if ( (ps_read_tasks_status(pid, ps)) == NULL) + { + ps->cswitch_vol = -1; + ps->cswitch_invol = -1; + + DEBUG("ps_read_tasks_status: not get context " + "switch data for pid %i",pid); + } + } + /* success */ return (0); } /* int ps_read_process (...) */ @@ -1766,6 +1903,9 @@ static int ps_read (void) 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++; break; diff --git a/src/types.db b/src/types.db index 38fb546c..b139393a 100644 --- a/src/types.db +++ b/src/types.db @@ -161,6 +161,8 @@ power value:GAUGE:0:U pressure value:GAUGE:0:U protocol_counter value:DERIVE:0:U ps_code value:GAUGE:0:9223372036854775807 +ps_cswitch_vol value:DERIVE:0:U +ps_cswitch_invol value:DERIVE:0:U ps_count processes:GAUGE:0:1000000, threads:GAUGE:0:1000000 ps_cputime user:DERIVE:0:U, syst:DERIVE:0:U ps_data value:GAUGE:0:9223372036854775807 -- 2.11.0