Code

Plugin process: collect voluntary and involuntary context switches.
authorManuel Luis Sanmartín Rozada <manuel.luis@gmail.com>
Wed, 20 May 2015 20:37:41 +0000 (22:37 +0200)
committerManuel Luis Sanmartín Rozada <manuel.luis@gmail.com>
Wed, 20 May 2015 20:37:41 +0000 (22:37 +0200)
src/collectd.conf.pod
src/processes.c
src/types.db

index 0c8e1e2011635a1b17361db5851e1fa1ff9ffe2c..1884914f4cb653faf4e9f4d1dde8e7a1a3c3732f 100644 (file)
@@ -5422,6 +5422,10 @@ dispatched to the daemon using the specified I<name> as an identifier. This
 allows to "group" several processes together. I<name> must not contain
 slashes.
 
+=item B<CollectContextSwitch> I<Boolean>
+
+Collect context switch of the process.
+
 =back
 
 =head2 Plugin C<protocols>
index 0649eab285770de13bfa61060ce3ee7eaa155b10..c1f92a7dc1e8c0bfe1742cc38c9d6fc3b267f5cd 100644 (file)
@@ -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;
index 38fb546c867170f43eada049718786e46e6a1060..b139393a4db1ba5218c38da1eaf591a769a95cf1 100644 (file)
@@ -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