[collectd] [PATCH] cgroup plugin: add new plugin for monitoring cgroup stats

Bruno Prémont bonbons at linux-vserver.org
Wed Mar 21 21:44:13 CET 2012


This new plugin adds support for monitoring (some) cgroup statistics.

It considers:
- memory controller
- cpuacct controller
- count of processes in cgroup

Each cgroup to be monitored has to be explicitly listed in,
configuration relatives paths are expected to be absolute,
usually in the form /sys/fs/cgroup/<cgroup>

Signed-off-by: Bruno Prémont <bonbons at linux-vserver.org>
---
diff -NurpP collectd-5.0.1.orig/configure.in collectd-5.0.1/configure.in
--- collectd-5.0.1.orig/configure.in	2011-10-31 19:42:23.597249677 +0100
+++ collectd-5.0.1/configure.in	2011-10-31 19:46:32.125788396 +0100
@@ -4321,6 +4321,7 @@ dependency_error="no"
 plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
+plugin_cgroup="no"
 plugin_conntrack="no"
 plugin_contextswitch="no"
 plugin_cpu="no"
@@ -4360,6 +4361,7 @@ plugin_zfs_arc="no"
 if test "x$ac_system" = "xLinux"
 then
 	plugin_battery="yes"
+	plugin_cgroup="yes"
 	plugin_conntrack="yes"
 	plugin_contextswitch="yes"
 	plugin_cpu="yes"
@@ -4620,6 +4622,7 @@ AC_PLUGIN([apple_sensors], [$with_libiok
 AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
 AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
+AC_PLUGIN([cgroup],      [$plugin_cgroup],     [Linux cgroup statistics])
 AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
 AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
 AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
diff -NurpP collectd-5.0.1.orig/src/cgroup.c collectd-5.0.1/src/cgroup.c
--- collectd-5.0.1.orig/src/cgroup.c	1970-01-01 01:00:00.000000000 +0100
+++ collectd-5.0.1/src/cgroup.c	2011-10-31 23:34:43.216403906 +0100
@@ -0,0 +1,275 @@
+/**
+ * collectd - src/cgroup.c
+ * Copyright (C) 2011  Bruno Prémont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the license is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Bruno Prémont <bonbons at linux-vserver.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define BUFSIZE 512
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+static const char *config_keys[] =
+{
+	"CGroup"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+typedef struct {
+	const char *name;
+	const char *path;
+	int fail_cnt;
+} cgroup_t;
+static cgroup_t **cgroup_list = NULL;
+int cgroup_num = 0;
+
+static int cgroup_config (const char *key, const char *value)
+{
+	if (strcasecmp (key, "CGroup") == 0)
+	{
+		cgroup_t *cg, **list;
+		char *tmp;
+		char *fields[2];
+		int   fields_num, value_len;
+
+		value_len = strlen(value)+1;
+		cg = malloc(value_len + sizeof(cgroup_t));
+		if (cg == NULL) 
+		{
+		    char errbuf[1024];
+		    ERROR ("malloc failed: %s",
+			    sstrerror (errno, errbuf, sizeof (errbuf)));
+		    return (1);
+		}
+		tmp = (char *)&cg[1];
+		memcpy(tmp, value, value_len);
+
+		fields_num = strsplit (tmp, fields, 2);
+		if (fields_num < 2)
+		{
+			free(cg);
+			return (1);
+		}
+		cg->name = fields[0];
+		cg->path = fields[1];
+		cg->fail_cnt = 0;
+
+		list = (cgroup_t **) realloc (cgroup_list, (cgroup_num + 1) * sizeof (cgroup_t *));
+		if (list == NULL)
+		{
+			char errbuf[1024];
+			ERROR ("realloc failed: %s",
+				sstrerror (errno, errbuf, sizeof (errbuf)));
+			free(cg);
+			return (1);
+		}
+
+		cgroup_list = list;
+		cgroup_list[cgroup_num] = cg;
+		cgroup_num++;
+	}
+
+	return (-1);
+}
+
+static int cgroup_read_memory (const char *cgroup, const char *file)
+{
+	FILE *fh;
+	char buffer[BUFSIZE];
+	char *cols[2];
+
+	value_t values[1];
+	value_list_t vl = VALUE_LIST_INIT;
+
+	vl.values = values;
+	vl.values_len = STATIC_ARRAY_SIZE (values);
+	sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+	sstrncpy (vl.plugin, "cgroup", sizeof (vl.plugin));
+	sstrncpy (vl.plugin_instance, cgroup, sizeof (vl.plugin_instance));
+	sstrncpy (vl.type, "memory", sizeof (vl.type));
+
+	if (NULL == (fh = fopen (file, "r")))
+	{
+		char errbuf[1024];
+		if (errno != ENOENT)
+			ERROR ("Cannot open '%s': %s", file,
+				sstrerror (errno, errbuf, sizeof (errbuf)));
+		return (1);
+	}
+
+	while (NULL != fgets (buffer, BUFSIZE, fh))
+	{
+		if (strsplit (buffer, cols, 2) < 2)
+			continue;
+
+		if (0 == strncmp (cols[0], "total_", 6))
+			continue;
+
+		values[0].gauge = atof (cols[1]);
+
+		sstrncpy (vl.type_instance, cols[0], sizeof (vl.type_instance));
+		plugin_dispatch_values (&vl);
+	} /* while (fgets) */
+
+	fclose (fh);
+	return 0;
+} /* int cgroup_read_memory */
+
+static int cgroup_read_procs (const char *cgroup, const char *file)
+{
+	FILE *fh;
+	char buffer[BUFSIZE];
+
+	value_t values[1];
+	value_list_t vl = VALUE_LIST_INIT;
+
+	vl.values = values;
+	vl.values_len = STATIC_ARRAY_SIZE (values);
+	sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+	sstrncpy (vl.plugin, "cgroup", sizeof (vl.plugin));
+	sstrncpy (vl.plugin_instance, cgroup, sizeof (vl.plugin_instance));
+	sstrncpy (vl.type, "vs_processes", sizeof (vl.type));
+	sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+
+	if (NULL == (fh = fopen (file, "r")))
+	{
+		char errbuf[1024];
+		if (errno != ENOENT)
+			ERROR ("Cannot open '%s': %s", file,
+				sstrerror (errno, errbuf, sizeof (errbuf)));
+		return (1);
+	}
+
+	values[0].gauge = 0;
+	while (NULL != fgets (buffer, BUFSIZE, fh))
+	{
+		values[0].gauge++;
+	} /* while (fgets) */
+
+	plugin_dispatch_values (&vl);
+	fclose (fh);
+	return 0;
+} /* int cgroup_read_memory */
+
+static int cgroup_read_cpuacct (const char *cgroup, const char *file)
+{
+	FILE *fh;
+	char buffer[BUFSIZE];
+	char *cols[2];
+
+	value_t values[1];
+	value_list_t vl = VALUE_LIST_INIT;
+
+	vl.values = values;
+	vl.values_len = STATIC_ARRAY_SIZE (values);
+	sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+	sstrncpy (vl.plugin, "cgroup", sizeof (vl.plugin));
+	sstrncpy (vl.plugin_instance, cgroup, sizeof (vl.plugin_instance));
+	sstrncpy (vl.type, "cpu", sizeof (vl.type));
+
+	if (NULL == (fh = fopen (file, "r")))
+	{
+		char errbuf[1024];
+		if (errno != ENOENT)
+			ERROR ("Cannot open '%s': %s", file,
+				sstrerror (errno, errbuf, sizeof (errbuf)));
+		return (1);
+	}
+
+	while (NULL != fgets (buffer, BUFSIZE, fh))
+	{
+		if (strsplit (buffer, cols, 2) < 2)
+			continue;
+
+		if (parse_value (cols[1], &values[0], DS_TYPE_DERIVE) != 0)
+			continue;
+
+		sstrncpy (vl.type_instance, cols[0], sizeof (vl.type_instance));
+		plugin_dispatch_values (&vl);
+	} /* while (fgets) */
+
+	fclose (fh);
+	return (0);
+} /* int cgroup_read_cpuacct */
+
+static int cgroup_read (void)
+{
+	int i;
+
+	for (i = 0; i < cgroup_num; i++)
+	{
+		int len;
+		char file[BUFSIZE];
+
+		if (access(cgroup_list[i]->path, R_OK | X_OK) != 0)
+		{
+			char errbuf[1024];
+			cgroup_list[i]->fail_cnt++;
+			if (cgroup_list[i]->fail_cnt != 1)
+				continue;
+			ERROR ("Cannot access '%s': %s", file,
+				sstrerror (errno, errbuf, sizeof (errbuf)));
+		}
+
+		len = ssnprintf (file, sizeof (file), "%s/%s", cgroup_list[i]->path, "memory.stat");
+		if ((len >= 0) && (len < BUFSIZE))
+			cgroup_read_memory(cgroup_list[i]->name, file);
+
+		len = ssnprintf (file, sizeof (file), "%s/%s", cgroup_list[i]->path, "cpuacct.stat");
+		if ((len >= 0) && (len < BUFSIZE))
+			cgroup_read_cpuacct(cgroup_list[i]->name, file);
+
+		len = ssnprintf (file, sizeof (file), "%s/%s", cgroup_list[i]->path, "cgroup.procs");
+		if ((len >= 0) && (len < BUFSIZE))
+			cgroup_read_procs(cgroup_list[i]->name, file);
+	}
+	return (0);
+} /* int cgroup_read */
+
+static int cgroup_shutdown (void)
+{
+	int i;
+
+	for (i = 0; i < cgroup_num; i++)
+	{
+		sfree(cgroup_list[i]);
+	}
+	sfree(cgroup_list);
+	cgroup_num = 0;
+
+	return (0);
+}
+
+void module_register (void)
+{
+	plugin_register_config ("cgroup", cgroup_config,
+			config_keys, config_keys_num);
+	plugin_register_read ("cgroup", cgroup_read);
+	plugin_register_shutdown ("cgroup", cgroup_shutdown);
+} /* void module_register(void) */
+
+/* vim: set ts=4 sw=4 noexpandtab : */
diff -NurpP collectd-5.0.1.orig/src/collectd.conf.in collectd-5.0.1/src/collectd.conf.in
--- collectd-5.0.1.orig/src/collectd.conf.in	2011-10-31 19:42:23.997215140 +0100
+++ collectd-5.0.1/src/collectd.conf.in	2011-10-31 22:27:23.381885858 +0100
@@ -59,6 +59,7 @@
 #@BUILD_PLUGIN_ASCENT_TRUE at LoadPlugin ascent
 #@BUILD_PLUGIN_BATTERY_TRUE at LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE at LoadPlugin bind
+#@BUILD_PLUGIN_CGROUP_TRUE at LoadPlugin cgroup
 #@BUILD_PLUGIN_CONNTRACK_TRUE at LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE at LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE at LoadPlugin cpu
@@ -210,6 +211,11 @@
 #  </View>
 #</Plugin>
 
+#<Plugin cgroup>
+#	CGroup root /sys/fs/cgroup
+#	CGroup test /sys/fs/cgroup/test
+#</Plugin>
+
 #<Plugin csv>
 #	DataDir "/var/lib/@PACKAGE_NAME@/csv"
 #	StoreRates false
diff -NurpP collectd-5.0.1.orig/src/collectd.conf.pod collectd-5.0.1/src/collectd.conf.pod
--- collectd-5.0.1.orig/src/collectd.conf.pod	2011-10-14 22:49:49.000000000 +0200
+++ collectd-5.0.1/src/collectd.conf.pod	2011-10-31 22:50:40.568106455 +0100
@@ -604,6 +604,25 @@ By default no detailed zone information
 
 =back
 
+=head2 Plugin C<cgroup>
+
+This plugin collects statistics about cgroups. It monitors B<"process count">
+(I<Path>/procs), B<"memory usage"> (I<Path>/memory.stat) and B<"CPU activity">
+(I<Path>/cpuacct.stat).
+
+Which ones are available depend on kernel support and mount options passed when
+mounting the cgroup filesystem to enable given sussystems.
+
+=over 1
+
+=item B<CGroup> I<Name> I<Path>
+
+Add a cgroup to monitor. I<Name> will get used as plugin-instance and I<Path>
+is the directory under which to look for statistics files (usually in the form
+F</sys/fs/cgroup/NAME>).
+
+=back
+
 =head2 Plugin C<cpufreq>
 
 This plugin doesn't have any options. It reads
diff -NurpP collectd-5.0.1.orig/src/Makefile.am collectd-5.0.1/src/Makefile.am
--- collectd-5.0.1.orig/src/Makefile.am	2011-10-31 19:42:23.427264356 +0100
+++ collectd-5.0.1/src/Makefile.am	2011-10-31 19:43:34.661113073 +0100
@@ -1031,6 +1031,14 @@ collectd_LDADD += "-dlopen" table.la
 collectd_DEPENDENCIES += table.la
 endif
 
+if BUILD_PLUGIN_CGROUP
+pkglib_LTLIBRARIES += cgroup.la
+cgroup_la_SOURCES = cgroup.c
+cgroup_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" cgroup.la
+collectd_DEPENDENCIES += cgroup.la
+endif
+
 if BUILD_PLUGIN_TAIL
 pkglib_LTLIBRARIES += tail.la
 tail_la_SOURCES = tail.c



More information about the collectd mailing list