[collectd] [PATCH] processes plugin: added AccountChildren option
Lukas Bednar
lbednar at redhat.com
Wed Sep 14 15:10:18 CEST 2011
From 97f3fd345ea0749908f4543e30fa5ab863a2575d Mon Sep 17 00:00:00 2001
From: Lukas Bednar <lbednar at 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
More information about the collectd
mailing list