[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