Code

irq plugin: Added plugin to collect interrupt counters.
authorPeter Holik <peter@holik.at>
Tue, 27 Feb 2007 08:51:59 +0000 (09:51 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Tue, 27 Feb 2007 08:51:59 +0000 (09:51 +0100)
-- 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:

 <Plugin irq>
        Irq 0
        Irq 14
        IgnoreSelected false
 </Plugin>

 collect all but no these irqs:

 <Plugin irq>
         Irq 7
         Irq 8
         Irq 9
         IgnoreSelected true
 </Plugin>

 cu Peter
-- >8 --

AUTHORS
collectd.spec
configure.in
contrib/collection.cgi
src/Makefile.am
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.pod
src/irq.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index b12387799f9a41782a7548c995f9c4dcab1374c0..b10958b1106fac9e2afbd33f306a4cf112b79df1 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,7 +4,7 @@ This package was written by:
 apcups plugin by:
   Anthony Gialluca <tonyabg at charter.net>
 
-cpufreq and multimeter module by:
+cpufreq, multimeter and irq module by:
   Peter Holik <peter at holik.at>
 
 hddtemp module by:
index 6116ffd667624107075b53e504d40124967a896f..b69d27a6c211b7912ffbffb4ab475cb0d7184516 100644 (file)
@@ -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*
index 602d0faa2db85fa242a138dff5dcbfac2a8ca65e..bebacd56eb50f8d5a09f94e18680e17f3ce3a13f 100644 (file)
@@ -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
index 5c8bd4dd1050ae4cdeb01d6a92fe1adc0f0ee012..2eb36722edc194f548455cb2066876f3b1b62431 100755 (executable)
@@ -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<div><a href="$MySelf$RelDir">Go up</a></div>\n);
 
                print "\t\t<ul>\n";
-               for (@{$files->{$Type}})
+               for (sort { $a <=> $b } @{$files->{$Type}})
                {
                        print qq(\t\t\t<li><a href="$MySelf$RelDir/$Type/$_">$_</a></li>\n);
                }
index 58cc0fe2a6ed845c9c19f7df10a50f0d4c5189b9..2e2d63716ca08c9c08e28eb36731011da08c6c31 100644 (file)
@@ -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
index 977dde2ff9818c6da8cb8fd91974493c8c0892f0..fcceb5412b5b407f70ddb32b29737a2b30c77e0f 100644 (file)
@@ -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
 #      Port 7634
 #</Plugin>
 
+#<Plugin irq>
+#      Irq 7
+#      Irq 8
+#      Irq 9
+#      IgnoreSelected true
+#</Plugin>
+
 #<Plugin mbmon>
 #      Host 127.0.0.1
 #      Port 411
index cffb7ad3ad31a1d1820b50bd9b6e2233ca3a03a1..1fba214ed48126d1e9ecaa81c6a75e7b435c465e 100644 (file)
@@ -240,6 +240,27 @@ TCP-Port to connect to. Defaults to B<7634>.
 
 =back
 
+=head2 Plugin C<irq>
+
+=over 4
+
+=item B<Irq> I<Irq>
+
+Select this irq. By default these irqs will then be collected. For a more
+detailed description see B<IgnoreSelected> below.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+If no configuration if given, the B<irq>-plugin will collect data from all
+irqs. This may not be practical, especially if no interrupts happen. Thus, you
+can use the B<Irq>-option to pick the interupt you're interested in.
+Sometimes, however, it's easier/prefered to collect all interupts I<except> a
+few ones. This option enables you to do that: By setting B<IgnoreSelected> to
+I<true> the effect of B<Irq> is inversed: All selected interupts are ignored
+and all other interupts are collected.
+
+=back
+
 =head2 Plugin C<mbmon>
 
 =over 4
index 8ee77ea26a8fec042a49d33eed80e0e99bf14f3b..ec235197b60c147609c8aed8ea403bf50a56d1ab 100644 (file)
@@ -52,6 +52,10 @@ Harddisk temperatures (I<hddtemp>)
 
 =item
 
+Irq (I<irq>)
+
+=item
+
 System load averages (I<load>)
 
 =item
@@ -264,6 +268,11 @@ which may interfere with other statistics..
 The B<hddtemp> homepage can be found at
 L<http://www.guzu.net/linux/hddtemp.php>.
 
+=head2 irq
+
+The B<irq> modules uses F</proc/interrupts> to retrieve interrupts per second.
+If there is more than one CPU all counters are added for each interrupt.
+
 =head2 vserver
 
 B<VServer> 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 (F<irq-I<E<lt>irqnumberE<gt>>-I<E<lt>descriptionE<gt>>.rrd>)
+
+  DS:value:COUNTER:HEARTBEAT:U:U
+
 =item System load (F<load.rrd>)
 
   DS:shortterm:GAUGE:HEARTBEAT:0:100
diff --git a/src/irq.c b/src/irq.c
new file mode 100644 (file)
index 0000000..34e02df
--- /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 <peter at holik.at>
+ **/
+
+#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