From f21bcfdca1cf55f420ac31ea933a93277a4f0458 Mon Sep 17 00:00:00 2001 From: Peter Holik Date: Tue, 27 Feb 2007 09:51:59 +0100 Subject: [PATCH] irq plugin: Added plugin to collect interrupt counters. -- 8< -- Hi! This plugin collects interrupts from /proc/interrupts Usually all interrupts are collected but you can configure what interruptnumbers are collected or not. Example: collect only wanted irqs: Irq 0 Irq 14 IgnoreSelected false collect all but no these irqs: Irq 7 Irq 8 Irq 9 IgnoreSelected true cu Peter -- >8 -- --- AUTHORS | 2 +- collectd.spec | 1 + configure.in | 2 + contrib/collection.cgi | 57 +++++++++- src/Makefile.am | 8 ++ src/collectd.conf.in | 8 ++ src/collectd.conf.pod | 21 ++++ src/collectd.pod | 13 +++ src/irq.c | 250 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 src/irq.c diff --git a/AUTHORS b/AUTHORS index b1238779..b10958b1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,7 +4,7 @@ This package was written by: apcups plugin by: Anthony Gialluca -cpufreq and multimeter module by: +cpufreq, multimeter and irq module by: Peter Holik hddtemp module by: diff --git a/collectd.spec b/collectd.spec index 6116ffd6..b69d27a6 100644 --- a/collectd.spec +++ b/collectd.spec @@ -80,6 +80,7 @@ rm -rf $RPM_BUILD_ROOT %attr(0444,root,root) %{_libdir}/%{name}/dns.so %attr(0444,root,root) %{_libdir}/%{name}/email.so %attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so* +%attr(0444,root,root) %{_libdir}/%{name}/irq.so* %attr(0444,root,root) %{_libdir}/%{name}/load.so* %attr(0444,root,root) %{_libdir}/%{name}/mbmon.so %attr(0444,root,root) %{_libdir}/%{name}/memory.so* diff --git a/configure.in b/configure.in index 602d0faa..bebacd56 100644 --- a/configure.in +++ b/configure.in @@ -1046,6 +1046,7 @@ AC_COLLECTD([dns], [disable], [module], [dns statistics]) AC_COLLECTD([email], [disable], [module], [email statistics]) AC_COLLECTD([quota], [enable], [module], [quota statistics (experimental)]) AC_COLLECTD([hddtemp], [disable], [module], [hdd temperature statistics]) +AC_COLLECTD([irq], [disable], [module], [irq statistics]) AC_COLLECTD([load], [disable], [module], [system load statistics]) AC_COLLECTD([mbmon], [disable], [module], [motherboard monitor statistics]) AC_COLLECTD([memory], [disable], [module], [memory statistics]) @@ -1099,6 +1100,7 @@ Configuration: dns . . . . . . . . $enable_dns email . . . . . . . $enable_email hddtemp . . . . . . $enable_hddtemp + irq . . . . . . . . $enable_irq load . . . . . . . $enable_load mbmon . . . . . . . $enable_mbmon memory . . . . . . $enable_memory diff --git a/contrib/collection.cgi b/contrib/collection.cgi index 5c8bd4dd..2eb36722 100755 --- a/contrib/collection.cgi +++ b/contrib/collection.cgi @@ -366,6 +366,16 @@ our $GraphDefs; 'GPRINT:temp_max:MAX:%4.1lf Max,', 'GPRINT:temp_avg:LAST:%4.1lf Last\l' ], + irq => ['DEF:irq_avg={file}:irq:AVERAGE', + 'DEF:irq_min={file}:irq:MIN', + 'DEF:irq_max={file}:irq:MAX', + "AREA:irq_max#$HalfBlue", + "AREA:irq_min#$Canvas", + "LINE1:irq_avg#$FullBlue:Interrupts", + 'GPRINT:irq_min:MIN:%5.1lf Min,', + 'GPRINT:irq_avg:AVERAGE:%5.1lf Avg,', + 'GPRINT:irq_max:MAX:%5.1lf Max,', + 'GPRINT:irq_avg:LAST:%5.1lf Last'], if_packets => ['DEF:tx_min={file}:tx:MIN', 'DEF:tx_avg={file}:tx:AVERAGE', 'DEF:tx_max={file}:tx:MAX', @@ -1185,6 +1195,7 @@ our $GraphArgs = fanspeed => ['-t', '{host} fanspeed {inst}', '-v', 'RPM'], frequency_offset => ['-t', 'NTPd frequency offset ({inst})', '-v', 'Parts per million'], hddtemp => ['-t', '{host} hdd temperature {inst}', '-v', '°Celsius'], + irq => ['-t', '{host} Interrupts {inst}', '-v', 'Ints/s'], if_errors => ['-t', '{host} {inst} errors', '-v', 'Errors/s'], if_packets => ['-t', '{host} {inst} packets', '-v', 'Packets/s'], load => ['-t', '{host} load average', '-v', 'System load', '-X', '0'], @@ -1229,6 +1240,7 @@ our $GraphMulti = disk => 1, email => \&output_graph_email_count, email_size => \&output_graph_email_size, + irq => \&output_graph_irq, spam_score => 1, spam_check => \&output_graph_spam_check, load => 0, @@ -1419,6 +1431,49 @@ sub output_graph_ping return (@ret); } +sub output_graph_irq +{ + my @inst = sort { $a <=> $b } @_; + my @ret = (); + + die if (@inst < 2); + + my @colors = get_n_colors (scalar (@inst)); + + for (my $i = 0; $i < scalar (@inst); $i++) + { + my $inst = $inst[$i]; + push (@ret, + "DEF:avg_$i=$AbsDir/irq-$inst.rrd:irq:AVERAGE", + "DEF:min_$i=$AbsDir/irq-$inst.rrd:irq:MIN", + "DEF:max_$i=$AbsDir/irq-$inst.rrd:irq:MAX"); + } + + for (my $i = 0; $i < scalar (@inst); $i++) + { + my $inst = $inst[$i]; + my $color = $colors[$i]; + + if (length ($inst) > 15) + { + $inst = substr ($inst, 0, 12) . '...'; + } + else + { + $inst = sprintf ('%-15s', $inst); + } + + push (@ret, + "LINE1:avg_$i#$color:$inst", + "GPRINT:min_$i:MIN:%5.1lf Min,", + "GPRINT:avg_$i:AVERAGE:%5.1lf Avg,", + "GPRINT:max_$i:MAX:%5.1lf Max,", + "GPRINT:avg_$i:LAST:%5.1lf Last\\l"); + } + + return (@ret); +} + sub output_graph_email_count { my @inst = @_; @@ -1828,7 +1883,7 @@ HEADER print qq(\t\t
Go up
\n); print "\t\t
    \n"; - for (@{$files->{$Type}}) + for (sort { $a <=> $b } @{$files->{$Type}}) { print qq(\t\t\t
  • $_
  • \n); } diff --git a/src/Makefile.am b/src/Makefile.am index 58cc0fe2..2e2d6371 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -208,6 +208,14 @@ collectd_LDADD += "-dlopen" hddtemp.la collectd_DEPENDENCIES += hddtemp.la endif +if BUILD_MODULE_IRQ +pkglib_LTLIBRARIES += irq.la +irq_la_SOURCES = irq.c +irq_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" irq.la +collectd_DEPENDENCIES += irq.la +endif + if BUILD_MODULE_LOAD pkglib_LTLIBRARIES += load.la load_la_SOURCES = load.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 977dde2f..fcceb541 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -30,6 +30,7 @@ @BUILD_MODULE_DISK_TRUE@LoadPlugin disk @BUILD_MODULE_DNS_TRUE@LoadPlugin dns @BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp +@BUILD_MODULE_IRQ_TRUE@LoadPlugin irq @BUILD_MODULE_LOAD_TRUE@LoadPlugin load @BUILD_MODULE_MBMON_TRUE@LoadPlugin mbmon @BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory @@ -70,6 +71,13 @@ # Port 7634 # +# +# Irq 7 +# Irq 8 +# Irq 9 +# IgnoreSelected true +# + # # Host 127.0.0.1 # Port 411 diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index cffb7ad3..1fba214e 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -240,6 +240,27 @@ TCP-Port to connect to. Defaults to B<7634>. =back +=head2 Plugin C + +=over 4 + +=item B I + +Select this irq. By default these irqs will then be collected. For a more +detailed description see B below. + +=item B I|I + +If no configuration if given, the B-plugin will collect data from all +irqs. This may not be practical, especially if no interrupts happen. Thus, you +can use the B-option to pick the interupt you're interested in. +Sometimes, however, it's easier/prefered to collect all interupts I a +few ones. This option enables you to do that: By setting B to +I the effect of B is inversed: All selected interupts are ignored +and all other interupts are collected. + +=back + =head2 Plugin C =over 4 diff --git a/src/collectd.pod b/src/collectd.pod index 8ee77ea2..ec235197 100644 --- a/src/collectd.pod +++ b/src/collectd.pod @@ -52,6 +52,10 @@ Harddisk temperatures (I) =item +Irq (I) + +=item + System load averages (I) =item @@ -264,6 +268,11 @@ which may interfere with other statistics.. The B homepage can be found at L. +=head2 irq + +The B modules uses F to retrieve interrupts per second. +If there is more than one CPU all counters are added for each interrupt. + =head2 vserver B support is only available for Linux. It cannot yet be found in a @@ -387,6 +396,10 @@ The DS'es depend on the module creating the RRD files: DS:value:GAUGE:HEARTBEAT:U:U +=item Irq (FirqnumberE>-IdescriptionE>.rrd>) + + DS:value:COUNTER:HEARTBEAT:U:U + =item System load (F) DS:shortterm:GAUGE:HEARTBEAT:0:100 diff --git a/src/irq.c b/src/irq.c new file mode 100644 index 00000000..34e02df3 --- /dev/null +++ b/src/irq.c @@ -0,0 +1,250 @@ +/** + * collectd - src/irq.c + * Copyright (C) 2007 Peter Holik + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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: + * Peter Holik + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#define MODULE_NAME "irq" + +#if KERNEL_LINUX +# define IRQ_HAVE_READ 1 +#else +# define IRQ_HAVE_READ 0 +#endif + +#define BUFSIZE 128 + +/* + * (Module-)Global variables + */ +static char *irq_file = "irq-%s.rrd"; + +static char *config_keys[] = +{ + "Irq", + "IgnoreSelected", + NULL +}; +static int config_keys_num = 2; + +static char *ds_def[] = +{ + "DS:irq:COUNTER:"COLLECTD_HEARTBEAT":0:U", + NULL +}; +static int ds_num = 1; + +static unsigned int *irq_list; +static unsigned int irq_list_num; + +static int base = 10; + +/* + * irq_list_action: + * 0 => default is to collect selected irqs + * 1 => ignore selcted irqs + */ +static int irq_list_action; + +static int irq_config (char *key, char *value) +{ + unsigned int *temp; + unsigned int irq; + char *endptr; + + if (strcasecmp (key, "Irq") == 0) + { + temp = (unsigned int *) realloc (irq_list, (irq_list_num + 1) * sizeof (unsigned int *)); + if (temp == NULL) + { + syslog (LOG_EMERG, "Cannot allocate more memory."); + return (1); + } + irq_list = temp; + + irq = strtol(value, &endptr, base); + + if (endptr == value || + (errno == ERANGE && (irq == LONG_MAX || irq == LONG_MIN)) || + (errno != 0 && irq == 0)) + { + syslog (LOG_EMERG, "Irq value is not a number."); + return (1); + } + irq_list[irq_list_num] = irq; + irq_list_num++; + } + else if (strcasecmp (key, "IgnoreSelected") == 0) + { + if ((strcasecmp (value, "True") == 0) + || (strcasecmp (value, "Yes") == 0) + || (strcasecmp (value, "On") == 0)) + irq_list_action = 1; + else + irq_list_action = 0; + } + else + { + return (-1); + } + return (0); +} + +/* + * Check if this interface/instance should be ignored. This is called from + * both, `submit' and `write' to give client and server the ability to + * ignore certain stuff.. + */ +static int check_ignore_irq (const unsigned int irq) +{ + int i; + + if (irq_list_num < 1) + return (0); + + for (i = 0; i < irq_list_num; i++) + if (irq == irq_list[i]) + return (irq_list_action); + + return (1 - irq_list_action); +} + +static void irq_write (char *host, char *inst, char *value) +{ + char file[BUFSIZE]; + int status; + + if (check_ignore_irq (atoi(inst))) + return; + + status = snprintf (file, BUFSIZE, irq_file, inst); + if (status < 1) + return; + else if (status >= BUFSIZE) + return; + + rrd_update_file (host, file, value, ds_def, ds_num); +} + +#if IRQ_HAVE_READ +static void irq_submit (unsigned int irq, unsigned int value, char *devices) +{ + char buf[BUFSIZE]; + char desc[BUFSIZE]; + int status; + + if (check_ignore_irq (irq)) + return; + + status = snprintf (buf, BUFSIZE, "%u:%u", + (unsigned int) curtime, value); + + if ((status >= BUFSIZE) || (status < 1)) + return; + + status = snprintf (desc, BUFSIZE, "%d-%s", irq, devices); + + if ((status >= BUFSIZE) || (status < 1)) + return; + + plugin_submit (MODULE_NAME, desc, buf); +} + +static void irq_read (void) +{ +#if KERNEL_LINUX + +#undef BUFSIZE +#define BUFSIZE 256 + + FILE *fh; + char buffer[BUFSIZE]; + unsigned int irq; + unsigned int irq_value; + long value; + char *ptr, *endptr; + + if ((fh = fopen ("/proc/interrupts", "r")) == NULL) + { + syslog (LOG_WARNING, "irq: fopen: %s", strerror (errno)); + return; + } + while (fgets (buffer, BUFSIZE, fh) != NULL) + { + errno = 0; /* To distinguish success/failure after call */ + irq = strtol(buffer, &endptr, base); + + if (endptr == buffer || + (errno == ERANGE && (irq == LONG_MAX || irq == LONG_MIN)) || + (errno != 0 && irq == 0)) continue; + + if (*endptr != ':') continue; + + ptr = ++endptr; + + irq_value = 0; + /* sum irq's for all CPUs */ + while (1) + { + errno = 0; + value = strtol(ptr, &endptr, base); + + if (endptr == ptr || + (errno == ERANGE && + (value == LONG_MAX || value == LONG_MIN)) || + (errno != 0 && value == 0)) break; + + irq_value += value; + ptr = endptr; + } + while (*ptr == ' ') ptr++; + while (*ptr && *ptr != ' ') ptr++; + while (*ptr == ' ') ptr++; + + if (!*ptr) continue; + + endptr = ptr; + + while (*(++endptr)) + if (!isalnum(*endptr)) *endptr='_'; + + ptr[strlen(ptr)-1] = '\0'; + + irq_submit (irq, irq_value, ptr); + } + fclose (fh); +#endif /* KERNEL_LINUX */ +} +#else +#define irq_read NULL +#endif /* IRQ_HAVE_READ */ + +void module_register (void) +{ + plugin_register (MODULE_NAME, NULL, irq_read, irq_write); + cf_register (MODULE_NAME, irq_config, config_keys, config_keys_num); +} + +#undef BUFSIZE +#undef MODULE_NAME -- 2.30.2