From: Florian Forster Date: Sat, 25 May 2013 18:20:48 +0000 (+0200) Subject: Merge branch 'more-varnish-stats' X-Git-Tag: collectd-5.4.0~36 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=e9cc06595b478d522773be7cd03423f84f8d43b6;hp=7e0f5825b622b655264858faf08b13331a95cca0;p=collectd.git Merge branch 'more-varnish-stats' --- diff --git a/AUTHORS b/AUTHORS index e04e1160..9df0f7ae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -76,6 +76,9 @@ Doug MacEachern Edward “Koko” Konetzko - fscache plugin. +Evan Felix + - mic plugin. + Fabian Linzberger - Percentage aggregation for `collectd-nagios'. @@ -129,6 +132,7 @@ Michael Hanselmann Michael Stapelberg - OpenBSD port of the tcpconns plugin. + - cgroups plugin. Michał Mirosław - thermal plugin. diff --git a/README b/README index 1c23aaab..04e87abf 100644 --- a/README +++ b/README @@ -37,6 +37,9 @@ Features Name server and resolver statistics from the `statistics-channel' interface of BIND 9.5, 9,6 and later. + - cgroups + CPU accounting information for process groups under Linux. + - conntrack Number of nf_conntrack entries. @@ -162,6 +165,10 @@ Features Memory utilization: Memory occupied by running processes, page cache, buffer cache and free. + - mic + Collects CPU usage, memory usage, temperatures and power consumption from + Intel Many Integrated Core (MIC) CPUs. + - modbus Reads values from Modbus/TCP enabled devices. Supports reading values from multiple "slaves" so gateway devices can be used. diff --git a/configure.in b/configure.in index 144f2897..ba52389e 100644 --- a/configure.in +++ b/configure.in @@ -4204,6 +4204,63 @@ fi AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes") # }}} +# --with-mic {{{ +with_mic_cflags="-I/opt/intel/mic/sysmgmt/sdk/include" +with_mic_ldpath="-L/opt/intel/mic/sysmgmt/sdk/lib/Linux" +with_mic_libs="" +AC_ARG_WITH(mic,[AS_HELP_STRING([--with-mic@<:@=PREFIX@:>@], [Path to Intel MIC Access API.])], +[ + if test "x$withval" = "xno" + then + with_mic="no" + else if test "x$withval" = "xyes" + then + with_mic="yes" + else if test -d "$with_mic/lib" + then + AC_MSG_NOTICE([Not checking for Intel Mic: Manually configured]) + with_mic_cflags="-I$withval/include" + with_mic_ldpath="-L$withval/lib/Linux" + with_mic_libs="-lMicAccessSDK -lscif -lpthread" + with_mic="yes" + fi; fi; fi +], +[with_mic="yes"]) +if test "x$with_mic" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_mic_cflags" + AC_CHECK_HEADERS(MicAccessApi.h,[],[with_mic="no (MicAccessApi not found)"]) + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_mic" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + + CPPFLAGS="$CPPFLAGS $with_mic_cflags" + LDFLAGS="$LDFLAGS $with_mic_ldpath" + + AC_CHECK_LIB(MicAccessSDK, MicInitAPI, + [with_mic_ldpath="$with_mic_ldpath" + with_mic_libs="-lMicAccessSDK -lscif -lpthread"], + [with_mic="no (symbol MicInitAPI not found)"],[-lscif -lpthread]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi + +if test "x$with_mic" = "xyes" +then + BUILD_WITH_MIC_CPPFLAGS="$with_mic_cflags" + BUILD_WITH_MIC_LIBPATH="$with_mic_ldpath" + BUILD_WITH_MIC_LDADD="$with_mic_libs" + AC_SUBST(BUILD_WITH_MIC_CPPFLAGS) + AC_SUBST(BUILD_WITH_MIC_LIBPATH) + AC_SUBST(BUILD_WITH_MIC_LDADD) +fi +#}}} + # --with-libvarnish {{{ with_libvarnish_cppflags="" with_libvarnish_cflags="" @@ -4630,6 +4687,7 @@ dependency_error="no" plugin_ascent="no" plugin_battery="no" plugin_bind="no" +plugin_cgroups="no" plugin_conntrack="no" plugin_contextswitch="no" plugin_cpu="no" @@ -4673,6 +4731,7 @@ then plugin_battery="yes" plugin_conntrack="yes" plugin_contextswitch="yes" + plugin_cgroups="yes" plugin_cpu="yes" plugin_cpufreq="yes" plugin_disk="yes" @@ -4956,6 +5015,7 @@ AC_PLUGIN([csv], [yes], [CSV output plugin]) AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics]) AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics]) AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics]) +AC_PLUGIN([cgroups], [$plugin_cgroups], [CGroups CPU usage accounting]) AC_PLUGIN([dbi], [$with_libdbi], [General database statistics]) AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics]) AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics]) @@ -4990,6 +5050,7 @@ AC_PLUGIN([md], [$have_linux_raid_md_u_h], [md (Linux software RAID) de AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) +AC_PLUGIN([mic], [$with_mic], [Intel Many Integrated Core stats]) AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin]) AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values]) AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics]) @@ -5225,6 +5286,7 @@ cat < + * Florian Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_mount.h" +#include "utils_ignorelist.h" + +static char const *config_keys[] = +{ + "CGroup", + "IgnoreSelected" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static ignorelist_t *il_cgroup = NULL; + +__attribute__ ((nonnull(1))) +__attribute__ ((nonnull(2))) +static void cgroups_submit_one (char const *plugin_instance, + char const *type_instance, value_t value) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &value; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "cgroups", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, plugin_instance, + sizeof (vl.plugin_instance)); + sstrncpy (vl.type, "cpu", sizeof (vl.type)); + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* void cgroups_submit_one */ + +/* + * This callback reads the user/system CPU time for each cgroup. + */ +static int read_cpuacct_procs (const char *dirname, char const *cgroup_name, + void *user_data) +{ + char abs_path[PATH_MAX]; + struct stat statbuf; + char buf[1024]; + int status; + + FILE *fh; + + if (ignorelist_match (il_cgroup, cgroup_name)) + return (0); + + ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, cgroup_name); + + status = lstat (abs_path, &statbuf); + if (status != 0) + { + ERROR ("cgroups plugin: stat (\"%s\") failed.", + abs_path); + return (-1); + } + + /* We are only interested in directories, so skip everything else. */ + if (!S_ISDIR (statbuf.st_mode)) + return (0); + + ssnprintf (abs_path, sizeof (abs_path), "%s/%s/cpuacct.stat", + dirname, cgroup_name); + fh = fopen (abs_path, "r"); + if (fh == NULL) + { + char errbuf[1024]; + ERROR ("cgroups pluign: fopen (\"%s\") failed: %s", + abs_path, + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + while (fgets (buf, sizeof (buf), fh) != NULL) + { + char *fields[8]; + int numfields = 0; + char *key; + size_t key_len; + value_t value; + + /* Expected format: + * + * user: 12345 + * system: 23456 + */ + strstripnewline (buf); + numfields = strsplit (buf, fields, STATIC_ARRAY_SIZE (fields)); + if (numfields != 2) + continue; + + key = fields[0]; + key_len = strlen (key); + if (key_len < 2) + continue; + + /* Strip colon off the first column */ + if (key[key_len - 1] != ':') + continue; + key[key_len - 1] = 0; + + status = parse_value (fields[1], &value, DS_TYPE_DERIVE); + if (status != 0) + continue; + + cgroups_submit_one (cgroup_name, key, value); + } + + fclose (fh); + return (0); +} /* int read_cpuacct_procs */ + +/* + * Gets called for every file/folder in /sys/fs/cgroup/cpu,cpuacct (or + * whereever cpuacct is mounted on the system). Calls walk_directory with the + * read_cpuacct_procs callback on every folder it finds, such as "system". + */ +static int read_cpuacct_root (const char *dirname, const char *filename, + void *user_data) +{ + char abs_path[PATH_MAX]; + struct stat statbuf; + int status; + + ssnprintf (abs_path, sizeof (abs_path), "%s/%s", dirname, filename); + + status = lstat (abs_path, &statbuf); + if (status != 0) + { + ERROR ("cgroups plugin: stat (%s) failed.", abs_path); + return (-1); + } + + if (S_ISDIR (statbuf.st_mode)) + { + status = walk_directory (abs_path, read_cpuacct_procs, + /* user_data = */ NULL, + /* include_hidden = */ 0); + return (status); + } + + return (0); +} + +static int cgroups_init (void) +{ + if (il_cgroup == NULL) + il_cgroup = ignorelist_create (1); + + return (0); +} + +static int cgroups_config (const char *key, const char *value) +{ + cgroups_init (); + + if (strcasecmp (key, "CGroup") == 0) + { + if (ignorelist_add (il_cgroup, value)) + return (1); + return (0); + } + else if (strcasecmp (key, "IgnoreSelected") == 0) + { + if (IS_TRUE (value)) + ignorelist_set_invert (il_cgroup, 0); + else + ignorelist_set_invert (il_cgroup, 1); + return (0); + } + + return (-1); +} + +static int cgroups_read (void) +{ + cu_mount_t *mnt_list; + cu_mount_t *mnt_ptr; + _Bool cgroup_found = 0; + + mnt_list = NULL; + if (cu_mount_getlist (&mnt_list) == NULL) + { + ERROR ("cgroups plugin: cu_mount_getlist failed."); + return (-1); + } + + for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next) + { + /* Find the cgroup mountpoint which contains the cpuacct + * controller. */ + if (strcmp(mnt_ptr->type, "cgroup") != 0 || + !cu_mount_getoptionvalue(mnt_ptr->options, "cpuacct")) + continue; + + walk_directory (mnt_ptr->dir, read_cpuacct_root, + /* user_data = */ NULL, + /* include_hidden = */ 0); + cgroup_found = 1; + /* It doesn't make sense to check other cpuacct mount-points + * (if any), they contain the same data. */ + break; + } + + cu_mount_freelist (mnt_list); + + if (!cgroup_found) + { + WARNING ("cgroups plugin: Unable to find cgroup " + "mount-point with the \"cpuacct\" option."); + return (-1); + } + + return (0); +} /* int cgroup_read */ + +void module_register (void) +{ + plugin_register_config ("cgroups", cgroups_config, + config_keys, config_keys_num); + plugin_register_init ("cgroups", cgroups_init); + plugin_register_read ("cgroups", cgroups_read); +} /* void module_register */ diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 467b4ce7..4a88e664 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -18,6 +18,13 @@ #TypesDB "@prefix@/share/@PACKAGE_NAME@/types.db" #----------------------------------------------------------------------------# +# When enabled, plugins are loaded automatically with the default options # +# when an appropriate block is encountered. # +# Disabled by default. # +#----------------------------------------------------------------------------# +#AutoLoadPlugin false + +#----------------------------------------------------------------------------# # Interval at which to query values. This may be overwritten on a per-plugin # # base by using the 'Interval' option of the LoadPlugin block: # # # @@ -72,6 +79,7 @@ #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch +#@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq @LOAD_PLUGIN_CSV@LoadPlugin csv @@ -157,6 +165,7 @@ #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid #@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish +#@BUILD_PLUGIN_MIC_TRUE@LoadPlugin mic #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem #@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver #@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless @@ -1027,6 +1036,24 @@ # UUIDFile "/etc/uuid" # +# +# ShowCPU true +# ShowCPUCores true +# ShowMemory true +# ShowTemperatures true +## Temperature Sensors can be ignored/shown by repeated #Temperature lines, and +## then inverted with a IgnoreSelectedTemperature. +## Known Temperature sensors: die, devmem, fin, fout, vccp, vddg, vddq +# Temperature vddg +# IgnoreSelectedTemperature true +# ShowPower true +## Power Sensors can be ignored/shown by repeated #Power lines, and +## then inverted with a IgnoreSelectedTemperature. +## Known Temperature sensors: total0, total1, inst, imax, pci3, c2x3, c2x4, vccp, vddg, vddq +# Power total1 +# IgnoreSelectedPower true +# + # # This tag support an argument if you want to # monitor the local instance just use diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 4d15fd23..432efb83 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -68,17 +68,33 @@ directory for the daemon. =item B I -Loads the plugin I. There must be at least one such line or B -will be mostly useless. +Loads the plugin I. This is required to load plugins, unless the +B option is enabled (see below). Without any loaded plugins, +I will be mostly useless. -Starting with collectd 4.9, this may also be a block in which further options -affecting the behavior of B may be specified. The following -options are allowed inside a B block: +Only the first B statement or block for a given plugin name has any +effect. This is useful when you want to split up the configuration into smaller +files and want each file to be "self contained", i.e. it contains a B +block I then appropriate B statement. The downside is that if +you have multiple conflicting B blocks, e.g. when they specify +different intervals, only one of them (the first one encountered) will take +effect and all others will be silently ignored. - - Globals true - Interval 10 - +B may either be a simple configuration I or a I +with additional options, affecting the behavior of B. A simple +statement looks like this: + + LoadPlugin "cpu" + +Options inside a B block can override default settings and +influence the way plugins are loaded, e.g.: + + + Globals true + Interval 60 + + +The following options are valid inside B blocks: =over 4 @@ -109,6 +125,19 @@ interval, that setting will take precedence. =back +=item B B|B + +When set to B (the default), each plugin needs to be loaded explicitly, +using the B statement documented above. If a +BPluginE...E> block is encountered and no configuration +handling callback for this plugin has been registered, a warning is logged and +the block is ignored. + +When set to B, explicit B statements are not required. Each +BPluginE...E> block acts as if it was immediately preceded by a +B statement. B statements are still required for +plugins that don't provide any configuration, e.g. the I. + =item B I [I] If I points to a file, includes that file. If I points to a @@ -852,6 +881,29 @@ By default no detailed zone information is collected. =back +=head2 Plugin C + +This plugin collects the CPU user/system time for each I by reading the +F files in the first cpuacct-mountpoint (typically +F on machines using systemd). + +=over 4 + +=item B I + +Select I based on the name. Whether only matching I are +collected or if they are ignored is controlled by the B option; +see below. + +=item B B|B + +Invert the selection: If set to true, all cgroups I the ones that +match any one of the criteria are collected. By default only selected +cgroups are collected if a selection is made. If no selection is configured +at all, B cgroups are selected. + +=back + =head2 Plugin C This plugin doesn't have any options. It reads @@ -2207,7 +2259,7 @@ interpreted. For a description of match blocks, please see L<"Plugin tail">. =head2 Plugin C -The C connects to a memcached server and queries statistics +The B connects to a memcached server and queries statistics about cache utilization, memory and bandwidth used. L @@ -2239,6 +2291,166 @@ setting is given, the B and B settings are ignored. =back +=head2 Plugin C + +The B gathers CPU statistics, memory usage and temperatures from +Intel's Many Integrated Core (MIC) systems. + +B + + + ShowCPU true + ShowCPUCores true + ShowMemory true + + ShowTemperatures true + Temperature vddg + Temperature vddq + IgnoreSelectedTemperature true + + ShowPower true + Power total0 + Power total1 + IgnoreSelectedPower true + + +The following options are valid inside the Bmic> block: + +=over 4 + +=item B B|B + +If enabled (the default) a sum of the CPU usage accross all cores is reported. + +=item B B|B + +If enabled (the default) per-core CPU usage is reported. + +=item B B|B + +If enabled (the default) the physical memory usage of the MIC system is +reported. + +=item B B|B + +If enabled (the default) various temperatures of the MIC system are reported. + +=item B I + +This option controls which temperatures are being reported. Whether matching +temperatures are being ignored or I matching temperatures are reported +depends on the B setting below. By default I +temperatures are reported. + +=item B B|B + +Controls the behavior of the B setting above. If set to B +(the default) only temperatures matching a B option are reported +or, if no B option is specified, all temperatures are reported. If +set to B, matching temperatures are I and all other temperatures +are reported. + +Known temperature names are: + +=over 4 + +=item die + +Die of the CPU + +=item devmem + +Device Memory + +=item fin + +Fan In + +=item fout + +Fan Out + +=item vccp + +Voltage ccp + +=item vddg + +Voltage ddg + +=item vddq + +Voltage ddq + +=back + +=item B B|B + +If enabled (the default) various temperatures of the MIC system are reported. + +=item B I + +This option controls which power readings are being reported. Whether matching +power readings are being ignored or I matching power readings are reported +depends on the B setting below. By default I +power readings are reported. + +=item B B|B + +Controls the behavior of the B setting above. If set to B +(the default) only power readings matching a B option are reported +or, if no B option is specified, all power readings are reported. If +set to B, matching power readings are I and all other power readings +are reported. + +Known power names are: + +=over 4 + +=item total0 + +Total power utilization averaged over Time Window 0 (uWatts). + +=item total1 + +Total power utilization averaged over Time Window 0 (uWatts). + +=item inst + +Instantaneous power (uWatts). + +=item imax + +Max instantaneous power (uWatts). + +=item pcie + +PCI-E connector power (uWatts). + +=item c2x3 + +2x3 connector power (uWatts). + +=item c2x4 + +2x4 connector power (uWatts). + +=item vccp + +Core rail (uVolts). + +=item vddg + +Uncore rail (uVolts). + +=item vddq + +Memory subsystem rail (uVolts). + +=back + +=back + =head2 Plugin C The B connects to a Modbus "slave" via Modbus/TCP and reads @@ -2246,7 +2458,7 @@ register values. It supports reading single registers (unsigned 16Ebit values), large integer values (unsigned 32Ebit values) and floating point values (two registers interpreted as IEEE floats in big endian notation). -Synopsis: +B RegisterBase 0 diff --git a/src/common.c b/src/common.c index d617832c..d963efa5 100644 --- a/src/common.c +++ b/src/common.c @@ -86,6 +86,47 @@ int ssnprintf (char *dest, size_t n, const char *format, ...) return (ret); } /* int ssnprintf */ +char *ssnprintf_alloc (char const *format, ...) /* {{{ */ +{ + char static_buffer[1024] = ""; + char *alloc_buffer; + size_t alloc_buffer_size; + int status; + va_list ap; + + /* Try printing into the static buffer. In many cases it will be + * sufficiently large and we can simply return a strdup() of this + * buffer. */ + va_start (ap, format); + status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap); + va_end (ap); + if (status < 0) + return (NULL); + + /* "status" does not include the null byte. */ + alloc_buffer_size = (size_t) (status + 1); + if (alloc_buffer_size <= sizeof (static_buffer)) + return (strdup (static_buffer)); + + /* Allocate a buffer large enough to hold the string. */ + alloc_buffer = malloc (alloc_buffer_size); + if (alloc_buffer == NULL) + return (NULL); + memset (alloc_buffer, 0, alloc_buffer_size); + + /* Print again into this new buffer. */ + va_start (ap, format); + status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap); + va_end (ap); + if (status < 0) + { + sfree (alloc_buffer); + return (NULL); + } + + return (alloc_buffer); +} /* }}} char *ssnprintf_alloc */ + char *sstrdup (const char *s) { char *r; @@ -1213,18 +1254,25 @@ int walk_directory (const char *dir, dirwalk_callback_f callback, return (0); } -int read_file_contents (const char *filename, char *buf, int bufsize) +ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize) { FILE *fh; - int n; + ssize_t ret; - if ((fh = fopen (filename, "r")) == NULL) - return -1; + fh = fopen (filename, "r"); + if (fh == NULL) + return (-1); - n = fread(buf, 1, bufsize, fh); - fclose(fh); + ret = (ssize_t) fread (buf, 1, bufsize, fh); + if ((ret == 0) && (ferror (fh) != 0)) + { + ERROR ("read_file_contents: Reading file \"%s\" failed.", + filename); + ret = -1; + } - return n; + fclose(fh); + return (ret); } counter_t counter_diff (counter_t old_value, counter_t new_value) diff --git a/src/common.h b/src/common.h index ae8e311f..317be8d1 100644 --- a/src/common.h +++ b/src/common.h @@ -56,7 +56,13 @@ struct rate_to_value_state_s typedef struct rate_to_value_state_s rate_to_value_state_t; char *sstrncpy (char *dest, const char *src, size_t n); + +__attribute__ ((format(printf,3,4))) int ssnprintf (char *dest, size_t n, const char *format, ...); + +__attribute__ ((format(printf,1,2))) +char *ssnprintf_alloc (char const *format, ...); + char *sstrdup(const char *s); void *smalloc(size_t size); char *sstrerror (int errnum, char *buf, size_t buflen); @@ -303,7 +309,7 @@ typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename, int walk_directory (const char *dir, dirwalk_callback_f callback, void *user_data, int hidden); /* Returns the number of bytes read or negative on error. */ -int read_file_contents (const char *filename, char *buf, int bufsize); +ssize_t read_file_contents (char const *filename, char *buf, size_t bufsize); counter_t counter_diff (counter_t old_value, counter_t new_value); diff --git a/src/configfile.c b/src/configfile.c index ac5e8edc..876ee23e 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -110,6 +110,7 @@ static cf_global_option_t cf_global_options[] = {"ReadThreads", NULL, "5"}, {"WriteThreads", NULL, "5"}, {"Timeout", NULL, "2"}, + {"AutoLoadPlugin", NULL, "false"}, {"PreCacheChain", NULL, "PreCache"}, {"PostCacheChain", NULL, "PostCache"} }; @@ -277,21 +278,6 @@ static int dispatch_loadplugin (const oconfig_item_t *ci) memset (&ctx, 0, sizeof (ctx)); ctx.interval = cf_get_default_interval (); - /* - * XXX: Magic at work: - * - * Some of the language bindings, for example the Python and Perl - * plugins, need to be able to export symbols to the scripts they run. - * For this to happen, the "Globals" flag needs to be set. - * Unfortunately, this technical detail is hard to explain to the - * average user and she shouldn't have to worry about this, ideally. - * So in order to save everyone's sanity use a different default for a - * handful of special plugins. --octo - */ - if ((strcasecmp ("Perl", name) == 0) - || (strcasecmp ("Python", name) == 0)) - flags |= PLUGIN_FLAGS_GLOBAL; - for (i = 0; i < ci->children_num; ++i) { if (strcasecmp("Globals", ci->children[i].key) == 0) cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL); @@ -394,6 +380,19 @@ static int dispatch_block_plugin (oconfig_item_t *ci) name = ci->values[0].value.string; + if (IS_TRUE (global_option_get ("AutoLoadPlugin"))) + { + int status; + + status = plugin_load (name, /* flags = */ 0); + if (status != 0) + { + ERROR ("Automatically loading plugin \"%s\" failed " + "with status %i.", name, status); + return (status); + } + } + /* Check for a complex callback first */ for (cb = complex_callback_head; cb != NULL; cb = cb->next) { diff --git a/src/curl_xml.c b/src/curl_xml.c index 0b413047..5adaf067 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -966,7 +966,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ if (status == 0) { user_data_t ud; - char cb_name[DATA_MAX_NAME_LEN]; + char *cb_name; if (db->instance == NULL) db->instance = strdup("default"); @@ -978,11 +978,10 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */ ud.data = (void *) db; ud.free_func = cx_free; - ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s", - db->instance, db->url); - - plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read, + cb_name = ssnprintf_alloc ("curl_xml-%s-%s", db->instance, db->url); + plugin_register_complex_read (/* group = */ "curl_xml", cb_name, cx_read, /* interval = */ NULL, &ud); + sfree (cb_name); } else { diff --git a/src/mic.c b/src/mic.c new file mode 100644 index 00000000..570da51b --- /dev/null +++ b/src/mic.c @@ -0,0 +1,417 @@ +/** + * collectd - src/mic.c + * Copyright (C) 2013 Battelle Memorial Institute + * + * 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: + * Evan Felix + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "utils_ignorelist.h" + +#include +#include +#include +#include +#include + +#define MAX_MICS 32 +#define MAX_CORES 256 + +static MicDeviceOnSystem mics[MAX_MICS]; +static U32 num_mics = 0; +static HANDLE mic_handle = NULL; + +static int const therm_ids[] = { + eMicThermalDie, eMicThermalDevMem, eMicThermalFin, eMicThermalFout, + eMicThermalVccp, eMicThermalVddg, eMicThermalVddq }; +static char const * const therm_names[] = { + "die", "devmem", "fin", "fout", + "vccp", "vddg", "vddq" }; + +static const char *config_keys[] = +{ + "ShowCPU", + "ShowCPUCores", + "ShowMemory", + "ShowTemperatures", + "Temperature", + "IgnoreSelectedTemperature", + "ShowPower", + "Power", + "IgnoreSelectedPower" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static _Bool show_cpu = 1; +static _Bool show_cpu_cores = 1; +static _Bool show_memory = 1; +static _Bool show_temps = 1; +static ignorelist_t *temp_ignore = NULL; +static _Bool show_power = 1; +static ignorelist_t *power_ignore = NULL; + +static int mic_init (void) +{ + U32 ret; + U32 mic_count; + + if (mic_handle) + return (0); + + mic_count = (U32) STATIC_ARRAY_SIZE(mics); + ret = MicInitAPI(&mic_handle, eTARGET_SCIF_DRIVER, mics, &mic_count); + if (ret != MIC_ACCESS_API_SUCCESS) { + ERROR("mic plugin: Problem initializing MicAccessAPI: %s", + MicGetErrorString(ret)); + } + DEBUG("mic plugin: found: %"PRIu32" MIC(s)",mic_count); + + if (mic_count<0 || mic_count>=MAX_MICS) { + ERROR("mic plugin: No Intel MICs in system"); + return (1); + } + else { + num_mics = mic_count; + return (0); + } +} + +static int mic_config (const char *key, const char *value) { + if (temp_ignore == NULL) + temp_ignore = ignorelist_create(1); + if (power_ignore == NULL) + power_ignore = ignorelist_create(1); + if (temp_ignore == NULL || power_ignore == NULL) + return (1); + + if (strcasecmp("ShowCPU",key) == 0) + { + show_cpu = IS_TRUE(value); + } + else if (strcasecmp("ShowCPUCores",key) == 0) + { + show_cpu_cores = IS_TRUE(value); + } + else if (strcasecmp("ShowTemperatures",key) == 0) + { + show_temps = IS_TRUE(value); + } + else if (strcasecmp("ShowMemory",key) == 0) + { + show_memory = IS_TRUE(value); + } + else if (strcasecmp("ShowPower",key) == 0) + { + show_power = IS_TRUE(value); + } + else if (strcasecmp("Temperature",key) == 0) + { + ignorelist_add(temp_ignore,value); + } + else if (strcasecmp("IgnoreSelectedTemperature",key) == 0) + { + int invert = 1; + if (IS_TRUE(value)) + invert = 0; + ignorelist_set_invert(temp_ignore,invert); + } + else if (strcasecmp("Power",key) == 0) + { + ignorelist_add(power_ignore,value); + } + else if (strcasecmp("IgnoreSelectedPower",key) == 0) + { + int invert = 1; + if (IS_TRUE(value)) + invert = 0; + ignorelist_set_invert(power_ignore,invert); + } + else + { + return (-1); + } + return (0); +} + +static void mic_submit_memory_use(int micnumber, const char *type_instance, U32 val) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + /* MicAccessAPI reports KB's of memory, adjust for this */ + DEBUG("mic plugin: Memory Value Report; %u %lf",val,((gauge_t)val)*1024.0); + values[0].gauge = ((gauge_t)val)*1024.0; + + vl.values=values; + vl.values_len=1; + + strncpy (vl.host, hostname_g, sizeof (vl.host)); + strncpy (vl.plugin, "mic", sizeof (vl.plugin)); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber); + strncpy (vl.type, "memory", sizeof (vl.type)); + strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +/* Gather memory Utilization */ +static int mic_read_memory(int mic) +{ + U32 ret; + U32 mem_total,mem_free,mem_bufs; + + ret = MicGetMemoryUtilization(mic_handle,&mem_total,&mem_free,&mem_bufs); + if (ret != MIC_ACCESS_API_SUCCESS) { + ERROR("mic plugin: Problem getting Memory Utilization: %s", + MicGetErrorString(ret)); + return (1); + } + mic_submit_memory_use(mic,"free",mem_free); + mic_submit_memory_use(mic,"used",mem_total-mem_free-mem_bufs); + mic_submit_memory_use(mic,"buffered",mem_bufs); + DEBUG("mic plugin: Memory Read: %u %u %u",mem_total,mem_free,mem_bufs); + return (0); +} + +static void mic_submit_temp(int micnumber, const char *type, gauge_t val) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = val; + + vl.values=values; + vl.values_len=1; + + strncpy (vl.host, hostname_g, sizeof (vl.host)); + strncpy (vl.plugin, "mic", sizeof (vl.plugin)); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%i", micnumber); + strncpy (vl.type, "temperature", sizeof (vl.type)); + strncpy (vl.type_instance, type, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +/* Gather Temperature Information */ +static int mic_read_temps(int mic) +{ + size_t num_therms = STATIC_ARRAY_SIZE(therm_ids); + size_t j; + + for (j = 0; j < num_therms; j++) { + U32 status; + U32 temp_buffer; + U32 buffer_size = (U32)sizeof(temp_buffer); + char const *name = therm_names[j]; + + if (ignorelist_match(temp_ignore, name) != 0) + continue; + + status = MicGetTemperature(mic_handle, therm_ids[j], + &temp_buffer, &buffer_size); + if (status != MIC_ACCESS_API_SUCCESS) { + ERROR("mic plugin: Error reading temperature \"%s\": " + "%s", name, MicGetErrorString(status)); + return (1); + } + mic_submit_temp(mic, name, temp_buffer); + } + return (0); +} + +static void mic_submit_cpu(int micnumber, const char *type_instance, + int core, derive_t val) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].derive = val; + + vl.values=values; + vl.values_len=1; + + strncpy (vl.host, hostname_g, sizeof (vl.host)); + strncpy (vl.plugin, "mic", sizeof (vl.plugin)); + if (core < 0) /* global aggregation */ + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%i", micnumber); + else /* per-core statistics */ + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%i-cpu-%i", micnumber, core); + strncpy (vl.type, "cpu", sizeof (vl.type)); + strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +/*Gather CPU Utilization Information */ +static int mic_read_cpu(int mic) +{ + MicCoreUtil core_util; + MicCoreJiff core_jiffs[MAX_CORES]; + U32 core_jiffs_size; + U32 status; + + core_jiffs_size = MAX_CORES * sizeof(MicCoreJiff); + status = MicGetCoreUtilization(mic_handle, &core_util, + core_jiffs, &core_jiffs_size); + if (status != MIC_ACCESS_API_SUCCESS) { + ERROR("mic plugin: Problem getting CPU utilization: %s", + MicGetErrorString(status)); + return(-1); + } + + if (show_cpu) { + mic_submit_cpu(mic, "user", -1, core_util.sum.user); + mic_submit_cpu(mic, "sys", -1, core_util.sum.sys); + mic_submit_cpu(mic, "nice", -1, core_util.sum.nice); + mic_submit_cpu(mic, "idle", -1, core_util.sum.idle); + } + + if (show_cpu_cores) { + int j; + for (j = 0; j < core_util.core; j++) { + mic_submit_cpu(mic, "user", j, core_jiffs[j].user); + mic_submit_cpu(mic, "sys", j, core_jiffs[j].sys); + mic_submit_cpu(mic, "nice", j, core_jiffs[j].nice); + mic_submit_cpu(mic, "idle", j, core_jiffs[j].idle); + } + } + return (0); +} + +static void mic_submit_power(int micnumber, const char *type, const char *type_instance, gauge_t val) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = val; + + vl.values=values; + vl.values_len=1; + + strncpy (vl.host, hostname_g, sizeof (vl.host)); + strncpy (vl.plugin, "mic", sizeof (vl.plugin)); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), "%i", micnumber); + strncpy (vl.type, type, sizeof (vl.type)); + strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +/* Gather Power Information */ +static int mic_read_power(int mic) +{ + U32 ret; + MicPwrUsage power_use; + + ret = MicGetPowerUsage(mic_handle,&power_use); + if (ret != MIC_ACCESS_API_SUCCESS) { + ERROR("mic plugin: Problem getting Power Usage: %s", + MicGetErrorString(ret)); + return (1); + } + + /* power is in uWatts, current in mA, voltage in uVolts.. convert to + * base unit */ + #define SUB_POWER(name) do { if (ignorelist_match(power_ignore,#name)==0) \ + mic_submit_power(mic,"power",#name,(gauge_t)power_use.name.prr*0.000001); \ + } while(0) + #define SUB_VOLTS(name) do { if (ignorelist_match(power_ignore,#name)==0) {\ + mic_submit_power(mic,"power",#name,(gauge_t)(power_use.name.pwr*0.000001)); \ + mic_submit_power(mic,"current",#name,(gauge_t)(power_use.name.cur*0.001)); \ + mic_submit_power(mic,"voltage",#name,(gauge_t)(power_use.name.volt*0.000001)); \ + }} while(0) + + SUB_POWER(total0); + SUB_POWER(total1); + SUB_POWER(inst); + SUB_POWER(imax); + SUB_POWER(pcie); + SUB_POWER(c2x3); + SUB_POWER(c2x4); + SUB_VOLTS(vccp); + SUB_VOLTS(vddg); + SUB_VOLTS(vddq); + + return (0); +} + +static int mic_read (void) +{ + int i; + U32 ret; + int error; + + error=0; + for (i=0;irf_name); + sfree (rf->rf_name); destroy_callback ((callback_func_t *) rf); rf = NULL; continue; @@ -830,8 +833,52 @@ void plugin_set_dir (const char *dir) } } +static _Bool plugin_is_loaded (char const *name) +{ + int status; + + if (plugins_loaded == NULL) + plugins_loaded = c_avl_create ((void *) strcasecmp); + assert (plugins_loaded != NULL); + + status = c_avl_get (plugins_loaded, name, /* ret_value = */ NULL); + return (status == 0); +} + +static int plugin_mark_loaded (char const *name) +{ + char *name_copy; + int status; + + name_copy = strdup (name); + if (name_copy == NULL) + return (ENOMEM); + + status = c_avl_insert (plugins_loaded, + /* key = */ name_copy, /* value = */ NULL); + return (status); +} + +static void plugin_free_loaded () +{ + void *key; + void *value; + + if (plugins_loaded == NULL) + return; + + while (c_avl_pick (plugins_loaded, &key, &value) == 0) + { + sfree (key); + assert (value == NULL); + } + + c_avl_destroy (plugins_loaded); + plugins_loaded = NULL; +} + #define BUFSIZE 512 -int plugin_load (const char *type, uint32_t flags) +int plugin_load (char const *plugin_name, uint32_t flags) { DIR *dh; const char *dir; @@ -843,15 +890,38 @@ int plugin_load (const char *type, uint32_t flags) struct dirent *de; int status; + if (plugin_name == NULL) + return (EINVAL); + + /* Check if plugin is already loaded and don't do anything in this + * case. */ + if (plugin_is_loaded (plugin_name)) + return (0); + dir = plugin_get_dir (); ret = 1; + /* + * XXX: Magic at work: + * + * Some of the language bindings, for example the Python and Perl + * plugins, need to be able to export symbols to the scripts they run. + * For this to happen, the "Globals" flag needs to be set. + * Unfortunately, this technical detail is hard to explain to the + * average user and she shouldn't have to worry about this, ideally. + * So in order to save everyone's sanity use a different default for a + * handful of special plugins. --octo + */ + if ((strcasecmp ("perl", plugin_name) == 0) + || (strcasecmp ("python", plugin_name) == 0)) + flags |= PLUGIN_FLAGS_GLOBAL; + /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the * type when matching the filename */ - status = ssnprintf (typename, sizeof (typename), "%s.so", type); + status = ssnprintf (typename, sizeof (typename), "%s.so", plugin_name); if ((status < 0) || ((size_t) status >= sizeof (typename))) { - WARNING ("plugin_load: Filename too long: \"%s.so\"", type); + WARNING ("plugin_load: Filename too long: \"%s.so\"", plugin_name); return (-1); } typename_len = strlen (typename); @@ -898,13 +968,14 @@ int plugin_load (const char *type, uint32_t flags) if (status == 0) { /* success */ + plugin_mark_loaded (plugin_name); ret = 0; break; } else { ERROR ("plugin_load: Load plugin \"%s\" failed with " - "status %i.", type, status); + "status %i.", plugin_name, status); } } @@ -912,7 +983,7 @@ int plugin_load (const char *type, uint32_t flags) if (filename[0] == 0) ERROR ("plugin_load: Could not find plugin \"%s\" in %s", - type, dir); + plugin_name, dir); return (ret); } @@ -1048,7 +1119,7 @@ int plugin_register_read (const char *name, rf->rf_udata.free_func = NULL; rf->rf_ctx = plugin_get_ctx (); rf->rf_group[0] = '\0'; - sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_name = strdup (name); rf->rf_type = RF_SIMPLE; rf->rf_interval = plugin_get_interval (); @@ -1080,7 +1151,7 @@ int plugin_register_complex_read (const char *group, const char *name, sstrncpy (rf->rf_group, group, sizeof (rf->rf_group)); else rf->rf_group[0] = '\0'; - sstrncpy (rf->rf_name, name, sizeof (rf->rf_name)); + rf->rf_name = strdup (name); rf->rf_type = RF_COMPLEX; if (interval != NULL) rf->rf_interval = TIMESPEC_TO_CDTIME_T (interval); @@ -1657,6 +1728,8 @@ void plugin_shutdown_all (void) destroy_all_callbacks (&list_notification); destroy_all_callbacks (&list_shutdown); destroy_all_callbacks (&list_log); + + plugin_free_loaded (); } /* void plugin_shutdown_all */ int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */ diff --git a/src/plugin.h b/src/plugin.h index 635ff308..8f0c6d86 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -223,7 +223,8 @@ void plugin_set_dir (const char *dir); * and a value below zero if an error occurs. * * NOTES - * No attempt is made to re-load an already loaded module. + * Re-loading an already loaded module is detected and zero is returned in + * this case. */ int plugin_load (const char *name, uint32_t flags); diff --git a/src/thermal.c b/src/thermal.c index 603f85bb..27c92bc7 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -80,12 +80,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir, if (device_list && ignorelist_match (device_list, name)) return -1; - len = snprintf (filename, sizeof (filename), + len = ssnprintf (filename, sizeof (filename), "%s/%s/temp", dirname_sysfs, name); if ((len < 0) || ((size_t) len >= sizeof (filename))) return -1; - len = read_file_contents (filename, data, sizeof(data)); + len = (ssize_t) read_file_contents (filename, data, sizeof(data)); if (len > 1 && data[--len] == '\n') { char *endptr = NULL; double temp; @@ -100,12 +100,12 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir, } } - len = snprintf (filename, sizeof (filename), + len = ssnprintf (filename, sizeof (filename), "%s/%s/cur_state", dirname_sysfs, name); if ((len < 0) || ((size_t) len >= sizeof (filename))) return -1; - len = read_file_contents (filename, data, sizeof(data)); + len = (ssize_t) read_file_contents (filename, data, sizeof(data)); if (len > 1 && data[--len] == '\n') { char *endptr = NULL; double state; @@ -139,12 +139,12 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir, * temperature: 55 C */ - len = snprintf (filename, sizeof (filename), + len = ssnprintf (filename, sizeof (filename), "%s/%s/temperature", dirname_procfs, name); if ((len < 0) || ((size_t) len >= sizeof (filename))) return -1; - len = read_file_contents (filename, data, sizeof(data)); + len = (ssize_t) read_file_contents (filename, data, sizeof(data)); if ((len > 0) && ((size_t) len > sizeof(str_temp)) && (data[--len] == '\n') && (! strncmp(data, str_temp, sizeof(str_temp)-1))) {