From 97f3fd345ea0749908f4543e30fa5ab863a2575d Mon Sep 17 00:00:00 2001
From: Lukas Bednar <lbed...@redhat.com>
Date: Wed, 14 Sep 2011 13:06:56 +0200
Subject: [PATCH] Subject: [PATCH] processes plugin: added AccountChildren
 option

This patch makes this plugin be able to computes summaries for children
of specific processes. Now it is only for linux platform. There is
ps_get_ppid(pid) function which is needed to implement for another
platforms. I have problem with peak caused by first sample and I don't
know how to fix it.
---
src/processes.c | 353 ++++++++++++++++++++++++++++++++++++++++++-------------
 src/rrdtool.c   |    3 +-
 2 files changed, 272 insertions(+), 84 deletions(-)

diff --git a/src/processes.c b/src/processes.c
index 8f4eb88..ae5d95b 100644
--- a/src/processes.c
+++ b/src/processes.c
@@ -161,6 +161,8 @@ typedef struct procstat
        regex_t *re;
 #endif

+       unsigned short children;
+
        unsigned long num_proc;
        unsigned long num_lwp;
        unsigned long vmem_size;
@@ -186,6 +188,12 @@ typedef struct procstat
 } procstat_t;

 static procstat_t *list_head_g = NULL;
+#if KERNEL_LINUX
+static procstat_entry_t *list_childs_head_g = NULL;
+/* FIXME: needed implement this functon for another platforms
+ *        now it is only for linux*/
+static int ps_get_ppid(unsigned int pid);
+#endif

 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -218,7 +226,7 @@ int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer,
 /* put name of process from config to list_head_g tree
    list_head_g is a list of 'procstat_t' structs with
    processes names we want to watch */
-static void ps_list_register (const char *name, const char *regexp)
+static void ps_list_register (const char *name, const char *regexp, unsigned short children)
 {
        procstat_t *new;
        procstat_t *ptr;
@@ -233,6 +241,8 @@ static void ps_list_register (const char *name, const char *regexp)
        memset (new, 0, sizeof (procstat_t));
        sstrncpy (new->name, name, sizeof (new->name));

+       new->children = children;
+
 #if HAVE_REGEX_H
        if (regexp != NULL)
        {
@@ -320,6 +330,34 @@ static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
        return (0);
 } /* int ps_list_match */

+static procstat_entry_t * ps_new_procstat_entry(procstat_entry_t **head, procstat_entry_t *last)
+{
+ procstat_entry_t * new = (procstat_entry_t*) malloc(sizeof (procstat_entry_t));
+       if (new != NULL)
+       {
+               memset (new, 0, sizeof (procstat_entry_t));
+
+               if(last == NULL)
+                       *head = new;
+               else
+                       last->next = new;
+       }
+       else
+               ERROR ("process plugin: ps_new_procstat_entry failed to allocate 
"
+                               "memory for new processes");
+       return new;
+}
+
+static procstat_entry_t *ps_find_procstat_entry(procstat_entry_t *pse, unsigned long id)
+{
+       for (;pse != NULL; pse = pse->next)
+       {
+               if (pse->id == id || pse->next == NULL)
+                       break;
+       }
+       return pse;
+}
+
 /* add process entry to 'instances' of process 'name' (or refresh it) */
static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
 {
@@ -331,29 +369,59 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t

        for (ps = list_head_g; ps != NULL; ps = ps->next)
        {
+#if KERNEL_LINUX
+               int current_pid = 0;
+               int ppid = entry->id;
+               int parent_found = 0;
+               unsigned short children = ps->children;
+
                if ((ps_list_match (name, cmdline, ps)) == 0)
-                       continue;
+               {
+                       while (children > 0 && parent_found == 0 && ppid != -1)
+                       {
+                               current_pid = ppid;
+                               for (pse = ps->instances; pse != NULL; pse = 
pse->next)
+                                       if (pse->id == current_pid)
+                                       {
+                                               parent_found = 1;
+                                               break;
+                                       }
+                                       else if (pse->next == NULL)
+                                               break;
+                                       ppid = ps_get_ppid(current_pid);
+                                       children--;
+                       }
+                       if (parent_found == 0)
+                                       continue;

-               for (pse = ps->instances; pse != NULL; pse = pse->next)
-                       if ((pse->id == entry->id) || (pse->next == NULL))
-                               break;
+                       pse = ps_find_procstat_entry(list_childs_head_g, 
entry->id);
+                       if (pse == NULL || (pse->id != entry->id))
+                       {
+                               pse = ps_new_procstat_entry 
(&list_childs_head_g, pse);
+                               if (pse == NULL)
+                                       return;

-               if ((pse == NULL) || (pse->id != entry->id))
+                               pse->id = entry->id;
+                       }
+               }
+               else
                {
-                       procstat_entry_t *new;
+                       pse = ps_find_procstat_entry(ps->instances, entry->id);
+               }
+#else
+               if ((ps_list_match (name, cmdline, ps)) == 0)
+                       continue;

-                       new = (procstat_entry_t *) malloc (sizeof 
(procstat_entry_t));
-                       if (new == NULL)
-                               return;
-                       memset (new, 0, sizeof (procstat_entry_t));
-                       new->id = entry->id;
+               pse = ps_find_procstat_entry(ps->instances, entry->id);
+#endif

+               if ((pse == NULL) || (pse->id != entry->id))
+               {
+                       pse = ps_new_procstat_entry (&ps->instances, pse);
                        if (pse == NULL)
-                               ps->instances = new;
-                       else
-                               pse->next = new;
+                               return;

-                       pse = new;
+                       pse->id = entry->id;
                }

                pse->age = 0;
@@ -418,7 +486,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t

                ps->vmem_minflt_counter += pse->vmem_minflt;
                ps->vmem_majflt_counter += pse->vmem_majflt;
-
+               
                if ((entry->cpu_user_counter == 0)
                                && (entry->cpu_system_counter == 0))
                {
@@ -458,12 +526,46 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
        }
 }

+static void ps_list_procstat_entry_reset(procstat_entry_t **head, const char *name)
+{
+       procstat_entry_t *pse = *head;
+       procstat_entry_t *pse_prev = NULL;
+               
+       while (pse != NULL)
+       {
+               if (pse->age > 10)
+               {
+                       DEBUG ("Removing this procstat entry cause it's too old: 
"
+                                       "id = %lu; name = %s;",
+                                       pse->id, name);
+
+                       if (pse_prev == NULL)
+                       {
+                               *head = pse->next;
+                               free (pse);
+                               pse = *head;
+                       }
+                       else
+                       {
+                               pse_prev->next = pse->next;
+                               free (pse);
+                               pse = pse_prev->next;
+                       }
+               }
+               else
+               {
+                       pse->age++;
+                       pse_prev = pse;
+                       pse = pse->next;
+               }
+       } /* while (pse != NULL) */
+
+}
+
 /* remove old entries from instances of processes in list_head_g */
 static void ps_list_reset (void)
 {
        procstat_t *ps;
-       procstat_entry_t *pse;
-       procstat_entry_t *pse_prev;

        for (ps = list_head_g; ps != NULL; ps = ps->next)
        {
@@ -479,43 +581,91 @@ static void ps_list_reset (void)
                ps->io_syscr = -1;
                ps->io_syscw = -1;

-               pse_prev = NULL;
-               pse = ps->instances;
-               while (pse != NULL)
+               ps_list_procstat_entry_reset (&ps->instances, ps->name);
+       } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
+#if KERNEL_LINUX
+       ps_list_procstat_entry_reset (&list_childs_head_g, "child of process");
+#endif
+}
+
+static unsigned short ps_config_tree(oconfig_item_t *c)
+{
+       unsigned short children = 0;
+
+#if KERNEL_LINUX
+       if (strcasecmp (c->key, "AccountChildren") == 0)
+       {
+               if (c->values_num != 1) {
+                       WARNING ("processes plugin: the `AccountChildren' expects 
"
+                                       "exactly one boolean or number argument (got 
%i). "
+                                       "-- use 'false' option by default", 
c->values_num);
+               }
+               
+               if (c->children_num != 0)
                {
-                       if (pse->age > 10)
-                       {
-                               DEBUG ("Removing this procstat entry cause it's too 
old: "
-                                               "id = %lu; name = %s;",
-                                               pse->id, ps->name);
+                       WARNING ("processes plugin: the `AccountChildren' config 
"
+                                       "option does not expect any child elements 
-- ignoring "
+                                       "content (%i elements) of the <Process '%s'> 
block.",
+                                       c->children_num, 
c->values[0].value.string);
+               }

-                               if (pse_prev == NULL)
-                               {
-                                       ps->instances = pse->next;
-                                       free (pse);
-                                       pse = ps->instances;
-                               }
-                               else
-                               {
-                                       pse_prev->next = pse->next;
-                                       free (pse);
-                                       pse = pse_prev->next;
-                               }
+               if (c->values[0].type == OCONFIG_TYPE_STRING)
+               {
+                       if (strcmp (c->values[0].value.string, "true") == 0)
+                       {
+                               children = (unsigned short) -1;
+                       }
+                       else if (strcmp (c->values[0].value.string, "false") != 
0)
+                       {
+                               WARNING ("processes plugin: the `AccountChildren' 
option "
+                                               "expects true|false options. got 
'%s' -- use "
+                                               "'false' option by default",
+                                               c->values[0].value.string);
+                       }
+               }
+               else if (c->values[0].type == OCONFIG_TYPE_NUMBER)
+               {
+                       if (c->values[0].value.number < 0)
+                       {
+                               WARNING ("processes plugin: the `AccountChildren' 
option "
+                                               "expects positive number, got '%f'. 
-- use '0' "
+                                               "option by default.", 
c->values[0].value.number);
                        }
                        else
                        {
-                               pse->age++;
-                               pse_prev = pse;
-                               pse = pse->next;
+                               children = (unsigned short) 
c->values[0].value.number;
                        }
-               } /* while (pse != NULL) */
-       } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
+               }
+               else if (c->values[0].type == OCONFIG_TYPE_BOOLEAN)
+               {
+                       if (c->values[0].value.boolean == 0)
+                               children = 0;
+                       else
+                               children = (unsigned short) -1;
+               }
+               else
+               {
+                       ERROR("processes plugin: unreachable statement");
+               }
+       }
+       else
+       {
+               WARNING("processes plugin: unexpected option '%s' -- ignoring", 
c->key);
+       }
+#else
+       WARNING ("processes plugin: the `AccountChildren' is supported only on "
+                       "linux platform. it will be used '0/false' value for this 
option "
+                       "by default.");
+#endif
+
+       return children;
 }

 /* put all pre-defined 'Process' names from config to list_head_g tree */
 static int ps_config (oconfig_item_t *ci)
 {
        int i;
+       unsigned short children = 0;

        for (i = 0; i < ci->children_num; ++i) {
                oconfig_item_t *c = ci->children + i;
@@ -531,13 +681,18 @@ static int ps_config (oconfig_item_t *ci)
                        }

                        if (c->children_num != 0) {
-                               WARNING ("processes plugin: the `Process' config 
option "
-                                               "does not expect any child elements 
-- ignoring "
-                                               "content (%i elements) of the <Process 
'%s'> block.",
-                                               c->children_num, 
c->values[0].value.string);
+                               if (c->children_num > 1)
+                               {
+                                       WARNING ("processes plugin: the `Process' 
config option "
+                                                       "expects at maximal one 
child elements -- use only "
+                                                       "first element, ignoring 
content (%i elements) of "
+                                                       "the <Process '%s'> 
block.",
+                                                       c->children_num - 1, 
c->values[0].value.string);
+                               }
+                               children = ps_config_tree(c->children);
                        }

-                       ps_list_register (c->values[0].value.string, NULL);
+                       ps_list_register (c->values[0].value.string, NULL, 
children);
                }
                else if (strcasecmp (c->key, "ProcessMatch") == 0)
                {
@@ -552,15 +707,22 @@ static int ps_config (oconfig_item_t *ci)
                        }

                        if (c->children_num != 0) {
-                               WARNING ("processes plugin: the `ProcessMatch' 
config option "
-                                               "does not expect any child elements 
-- ignoring "
-                                               "content (%i elements) of the 
<ProcessMatch '%s' '%s'> "
-                                               "block.", c->children_num, 
c->values[0].value.string,
-                                               c->values[1].value.string);
+                               if (c->children_num > 1)
+                               {
+                                       WARNING ("processes plugin: the 
`ProcessMatch' config "
+                                                       "option expects at maximal 
one child element "
+                                                       "-- use only first element, 
ignoring content "
+                                                       "(%i elements) of the 
<ProcessMatch '%s' '%s'> "
+                                                       "block.", 
c->children_num - 1,
+                                                   c->values[0].value.string,
+                                                       
c->values[1].value.string);
+                               }
+
+                               children = ps_config_tree(c->children);
                        }

                        ps_list_register (c->values[0].value.string,
-                                       c->values[1].value.string);
+                                       c->values[1].value.string, children);
                }
                else
                {
@@ -712,7 +874,7 @@ static void ps_submit_proc_list (procstat_t *ps)
        }

        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
- "vmem_size = %lu; vmem_rss = %lu; vmem_data = %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"; "
@@ -728,6 +890,49 @@ static void ps_submit_proc_list (procstat_t *ps)

/* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
 #if KERNEL_LINUX
+static int ps_read_stat(unsigned int pid, char **fields, int fields_len)
+{
+       char filename[PATH_MAX];
+       char buffer[1024];
+       int i;
+       
+       ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
+
+       i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
+       if (i <= 0)
+               return (-1);
+       buffer[i] = 0;
+
+       fields_len = strsplit (buffer, fields, fields_len);
+       if (fields_len < 24)
+       {
+               DEBUG ("processes plugin: ps_read_process (pid = %i):"
+                               " `%s' has only %i fields..",
+                               (int) pid, filename, fields_len);
+               return (-1);
+       }
+       return fields_len;
+}
+
+static int ps_get_ppid(unsigned int pid)
+{
+       int ppid;
+       char *fields[64];
+       int fields_len = ps_read_stat(pid, fields, STATIC_ARRAY_SIZE(fields));
+       if (fields_len != -1)
+       {
+               ppid = atoi (fields[3]);
+               if (ppid > 0)
+                       return ppid;
+       }
+       else
+       {
+               DEBUG ("processes plugin: ps_get_ppid (pid = %i) can't retrieve 
PPID",
+                               pid);
+       }
+       return (-1);
+}
+
 static int ps_read_tasks (int pid)
 {
        char           dirname[64];
@@ -780,7 +985,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
                        continue;

                numfields = strsplit (buffer, fields,
-                                      STATIC_ARRAY_SIZE (fields));
+                                                                               
STATIC_ARRAY_SIZE (fields));

                if (numfields < 2)
                        continue;
@@ -875,14 +1080,9 @@ static procstat_t *ps_read_io (int pid, procstat_t *ps)

 int ps_read_process (int pid, procstat_t *ps, char *state)
 {
-       char  filename[64];
-       char  buffer[1024];
-
        char *fields[64];
        char  fields_len;

-       int   i;
-
        int   name_len;

        derive_t cpu_user_counter;
@@ -892,22 +1092,10 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        long long unsigned stack_size;

        memset (ps, 0, sizeof (procstat_t));
-
-       ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
-
-       i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
-       if (i <= 0)
-               return (-1);
-       buffer[i] = 0;
-
-       fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
-       if (fields_len < 24)
-       {
-               DEBUG ("processes plugin: ps_read_process (pid = %i):"
-                               " `%s' has only %i fields..",
-                               (int) pid, filename, fields_len);
-               return (-1);
-       }
+       
+       fields_len = ps_read_stat(pid, fields, STATIC_ARRAY_SIZE(fields));
+       if (fields_len == -1)
+               return fields_len;

        /* copy the name, strip brackets in the process */
        name_len = strlen (fields[1]) - 2;
@@ -1468,7 +1656,6 @@ static int ps_read (void)
        int stopped  = 0;
        int paging   = 0;
        int blocked  = 0;
-
        struct dirent *ent;
        DIR           *proc;
        int            pid;
@@ -1581,9 +1768,9 @@ static int ps_read (void)
        char errbuf[1024];
        char cmdline[ARG_MAX];
        char *cmdline_ptr;
-       struct kinfo_proc *procs;          /* array of processes */
-       char **argv;
-       int count;                         /* returns number of processes */
+       struct kinfo_proc *procs;          /* array of processes */
+       char **argv;
+       int count;                         /* returns number of processes */
        int i;

        procstat_t *ps_ptr;
@@ -1740,7 +1927,7 @@ static int ps_read (void)
                                if (procentry[i].pi_pid == 0)
                                        cmdline = "swapper";
                                cargs = cmdline;
-                       }
+                       }
                        else
                        {
if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
diff --git a/src/rrdtool.c b/src/rrdtool.c
index 56a82d0..06ca934 100644
--- a/src/rrdtool.c
+++ b/src/rrdtool.c
@@ -892,7 +892,8 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl,
                return (0);

        if (0 != strcmp (ds->type, vl->type)) {
-               ERROR ("rrdtool plugin: DS type does not match value list 
type");
+               ERROR ("rrdtool plugin: DS type does not match value list type: 
"
+                               "'%s' != '%s'", ds->type, vl->type);
                return -1;
        }

--
1.7.4.4


_______________________________________________
collectd mailing list
collectd@verplant.org
http://mailman.verplant.org/listinfo/collectd

Reply via email to