79dc15070e5f551f6fd57395c666c11b9745a5f0
[collectd.git] / src / cpu.c
1 /**
2  * collectd - src/cpu.c
3  * Copyright (C) 2005-2010  Florian octo Forster
4  * Copyright (C) 2008       Oleg King
5  * Copyright (C) 2009       Simon Kuhnle
6  * Copyright (C) 2009       Manuel Sanmartin
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Florian octo Forster <octo at verplant.org>
23  *   Oleg King <king2 at kaluga.ru>
24  *   Simon Kuhnle <simon at blarzwurst.de>
25  *   Manuel Sanmartin
26  **/
27
28 #include "collectd.h"
29 #include "common.h"
30 #include "plugin.h"
31
32 #ifdef HAVE_MACH_KERN_RETURN_H
33 # include <mach/kern_return.h>
34 #endif
35 #ifdef HAVE_MACH_MACH_INIT_H
36 # include <mach/mach_init.h>
37 #endif
38 #ifdef HAVE_MACH_HOST_PRIV_H
39 # include <mach/host_priv.h>
40 #endif
41 #if HAVE_MACH_MACH_ERROR_H
42 #  include <mach/mach_error.h>
43 #endif
44 #ifdef HAVE_MACH_PROCESSOR_INFO_H
45 # include <mach/processor_info.h>
46 #endif
47 #ifdef HAVE_MACH_PROCESSOR_H
48 # include <mach/processor.h>
49 #endif
50 #ifdef HAVE_MACH_VM_MAP_H
51 # include <mach/vm_map.h>
52 #endif
53
54 #ifdef HAVE_LIBKSTAT
55 # include <sys/sysinfo.h>
56 #endif /* HAVE_LIBKSTAT */
57
58 #if (defined(HAVE_SYSCTL) && HAVE_SYSCTL) \
59         || (defined(HAVE_SYSCTLBYNAME) && HAVE_SYSCTLBYNAME)
60 # ifdef HAVE_SYS_SYSCTL_H
61 #  include <sys/sysctl.h>
62 # endif
63
64 # ifdef HAVE_SYS_DKSTAT_H
65 #  include <sys/dkstat.h>
66 # endif
67
68 # if !defined(CP_USER) || !defined(CP_NICE) || !defined(CP_SYS) || !defined(CP_INTR) || !defined(CP_IDLE) || !defined(CPUSTATES)
69 #  define CP_USER   0
70 #  define CP_NICE   1
71 #  define CP_SYS    2
72 #  define CP_INTR   3
73 #  define CP_IDLE   4
74 #  define CPUSTATES 5
75 # endif
76 #endif /* HAVE_SYSCTL || HAVE_SYSCTLBYNAME */
77
78 #if HAVE_SYSCTL
79 # if defined(CTL_HW) && defined(HW_NCPU) \
80         && defined(CTL_KERN) && defined(KERN_CPTIME) && defined(CPUSTATES)
81 #  define CAN_USE_SYSCTL 1
82 # else
83 #  define CAN_USE_SYSCTL 0
84 # endif
85 #else
86 # define CAN_USE_SYSCTL 0
87 #endif
88
89 #define CPU_SUBMIT_USER 0
90 #define CPU_SUBMIT_SYSTEM 1
91 #define CPU_SUBMIT_WAIT 2
92 #define CPU_SUBMIT_NICE 3
93 #define CPU_SUBMIT_SWAP 4
94 #define CPU_SUBMIT_INTERRUPT 5
95 #define CPU_SUBMIT_SOFTIRQ 6
96 #define CPU_SUBMIT_STEAL 7
97 #define CPU_SUBMIT_IDLE 8
98 #define CPU_SUBMIT_ACTIVE 9
99 #define CPU_SUBMIT_MAX 10
100
101 #if HAVE_STATGRAB_H
102 # include <statgrab.h>
103 #endif
104
105 # ifdef HAVE_PERFSTAT
106 #  include <sys/protosw.h>
107 #  include <libperfstat.h>
108 # endif /* HAVE_PERFSTAT */
109
110 #if !PROCESSOR_CPU_LOAD_INFO && !KERNEL_LINUX && !HAVE_LIBKSTAT \
111         && !CAN_USE_SYSCTL && !HAVE_SYSCTLBYNAME && !HAVE_LIBSTATGRAB && !HAVE_PERFSTAT
112 # error "No applicable input method."
113 #endif
114
115 static const char *cpu_state_names[] = {
116         "user",
117         "system",
118         "wait",
119         "nice",
120         "swap",
121         "interrupt",
122         "softirq",
123         "steal",
124         "idle",
125         "active"
126 };
127
128 #ifdef PROCESSOR_CPU_LOAD_INFO
129 static mach_port_t port_host;
130 static processor_port_array_t cpu_list;
131 static mach_msg_type_number_t cpu_list_len;
132
133 #if PROCESSOR_TEMPERATURE
134 static int cpu_temp_retry_counter = 0;
135 static int cpu_temp_retry_step    = 1;
136 static int cpu_temp_retry_max     = 1;
137 #endif /* PROCESSOR_TEMPERATURE */
138 /* #endif PROCESSOR_CPU_LOAD_INFO */
139
140 #elif defined(KERNEL_LINUX)
141 /* no variables needed */
142 /* #endif KERNEL_LINUX */
143
144 #elif defined(HAVE_LIBKSTAT)
145 /* colleague tells me that Sun doesn't sell systems with more than 100 or so CPUs.. */
146 # define MAX_NUMCPU 256
147 extern kstat_ctl_t *kc;
148 static kstat_t *ksp[MAX_NUMCPU];
149 static int numcpu;
150 /* #endif HAVE_LIBKSTAT */
151
152 #elif CAN_USE_SYSCTL
153 static int numcpu;
154 /* #endif CAN_USE_SYSCTL */
155
156 #elif defined(HAVE_SYSCTLBYNAME)
157 static int numcpu;
158 #  ifdef HAVE_SYSCTL_KERN_CP_TIMES
159 static int maxcpu;
160 #  endif /* HAVE_SYSCTL_KERN_CP_TIMES */
161 /* #endif HAVE_SYSCTLBYNAME */
162
163 #elif defined(HAVE_LIBSTATGRAB)
164 /* no variables needed */
165 /* #endif  HAVE_LIBSTATGRAB */
166
167 #elif defined(HAVE_PERFSTAT)
168 static perfstat_cpu_t *perfcpu;
169 static int numcpu;
170 static int pnumcpu;
171 #endif /* HAVE_PERFSTAT */
172
173 static value_to_rate_state_t *percents = NULL;
174 static gauge_t agg_percents[CPU_SUBMIT_MAX] = {
175         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
176
177 };
178 static int percents_cells = 0;
179 static int cpu_count = 0;
180
181
182 static _Bool report_by_cpu = 1;
183 static _Bool report_percent = 0;
184 static _Bool report_active = 0;
185
186 static const char *config_keys[] =
187 {
188         "ReportByCpu",
189         "ReportActive",
190         "ValuesPercentage"
191 };
192 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
193
194
195 static int cpu_config (const char *key, const char *value)
196 {
197         if (strcasecmp (key, "ReportByCpu") == 0) {
198                 report_by_cpu = IS_TRUE (value) ? 1 : 0;
199                 if (!report_by_cpu)
200                         report_percent = 1;
201         }
202         if (strcasecmp (key, "ValuesPercentage") == 0) {
203                 report_percent = IS_TRUE (value) ? 1 : 0;
204                 if (!report_percent)
205                         report_by_cpu = 1;
206         }
207         if (strcasecmp (key, "ReportActive") == 0)
208                 report_active = IS_TRUE (value) ? 1 : 0;
209         return (-1);
210 }
211
212 static int cpu_states_grow (void)
213 {
214   void *tmp;
215   int size;
216   int i;
217
218   size = cpu_count * CPU_SUBMIT_MAX; /* always alloc for all states */
219
220   if (size <= 0)
221           return 0;
222
223   if (percents_cells >= size)
224           return 0;
225
226   if (percents == NULL) {
227           percents = malloc(size * sizeof(*percents));
228           if (percents == NULL)
229                   return -1;
230           for (i = 0; i < size; i++)
231                   memset(&percents[i], 0, sizeof(*percents));
232           percents_cells = size;
233           return 0;
234   }
235
236   tmp = realloc(percents, size * sizeof(*percents));
237
238   if (tmp == NULL) {
239           ERROR ("cpu plugin: could not reserve enough space to hold states");
240           percents = NULL;
241           return -1;
242   }
243
244   percents = tmp;
245
246   for (i = percents_cells ; i < size; i++)
247           memset(&percents[i], 0, sizeof(*percents));
248
249   percents_cells = size;
250   return 0;
251 } /* cpu_states_grow */
252
253
254 static int init (void)
255 {
256 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
257         kern_return_t status;
258
259         port_host = mach_host_self ();
260
261         /* FIXME: Free `cpu_list' if it's not NULL */
262         if ((status = host_processors (port_host, &cpu_list, &cpu_list_len)) != KERN_SUCCESS)
263         {
264                 ERROR ("cpu plugin: host_processors returned %i", (int) status);
265                 cpu_list_len = 0;
266                 return (-1);
267         }
268
269         DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
270         INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
271
272         cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (plugin_get_interval ());
273 /* #endif PROCESSOR_CPU_LOAD_INFO */
274
275 #elif defined(HAVE_LIBKSTAT)
276         kstat_t *ksp_chain;
277
278         numcpu = 0;
279
280         if (kc == NULL)
281                 return (-1);
282
283         /* Solaris doesn't count linear.. *sigh* */
284         for (numcpu = 0, ksp_chain = kc->kc_chain;
285                         (numcpu < MAX_NUMCPU) && (ksp_chain != NULL);
286                         ksp_chain = ksp_chain->ks_next)
287                 if (strncmp (ksp_chain->ks_module, "cpu_stat", 8) == 0)
288                         ksp[numcpu++] = ksp_chain;
289 /* #endif HAVE_LIBKSTAT */
290
291 #elif CAN_USE_SYSCTL
292         size_t numcpu_size;
293         int mib[2] = {CTL_HW, HW_NCPU};
294         int status;
295
296         numcpu = 0;
297         numcpu_size = sizeof (numcpu);
298
299         status = sysctl (mib, STATIC_ARRAY_SIZE (mib),
300                         &numcpu, &numcpu_size, NULL, 0);
301         if (status == -1)
302         {
303                 char errbuf[1024];
304                 WARNING ("cpu plugin: sysctl: %s",
305                                 sstrerror (errno, errbuf, sizeof (errbuf)));
306                 return (-1);
307         }
308 /* #endif CAN_USE_SYSCTL */
309
310 #elif defined (HAVE_SYSCTLBYNAME)
311         size_t numcpu_size;
312
313         numcpu_size = sizeof (numcpu);
314
315         if (sysctlbyname ("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0)
316         {
317                 char errbuf[1024];
318                 WARNING ("cpu plugin: sysctlbyname(hw.ncpu): %s",
319                                 sstrerror (errno, errbuf, sizeof (errbuf)));
320                 return (-1);
321         }
322
323 #ifdef HAVE_SYSCTL_KERN_CP_TIMES
324         numcpu_size = sizeof (maxcpu);
325
326         if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &numcpu_size, NULL, 0) < 0)
327         {
328                 char errbuf[1024];
329                 WARNING ("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s",
330                                 sstrerror (errno, errbuf, sizeof (errbuf)));
331                 return (-1);
332         }
333 #else
334         if (numcpu != 1)
335                 NOTICE ("cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
336 #endif
337 /* #endif HAVE_SYSCTLBYNAME */
338
339 #elif defined(HAVE_LIBSTATGRAB)
340         /* nothing to initialize */
341 /* #endif HAVE_LIBSTATGRAB */
342
343 #elif defined(HAVE_PERFSTAT)
344         /* nothing to initialize */
345 #endif /* HAVE_PERFSTAT */
346
347         return (0);
348 } /* int init */
349
350 static void submit_value (int cpu_num, int cpu_state, const char *type, value_t value)
351 {
352         value_t values[1];
353         value_list_t vl = VALUE_LIST_INIT;
354
355         memcpy(&values[0], &value, sizeof(value));
356
357         vl.values = values;
358         vl.values_len = 1;
359
360         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
361         sstrncpy (vl.plugin, "cpu", sizeof (vl.plugin));
362         sstrncpy (vl.type, type, sizeof (vl.type));
363         sstrncpy (vl.type_instance, cpu_state_names[cpu_state],
364                   sizeof (vl.type_instance));
365
366         if (cpu_num >= 0) {
367                 ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
368                            "%i", cpu_num);
369         }
370         plugin_dispatch_values (&vl);
371 }
372
373 static void submit_percent(int cpu_num, int cpu_state, gauge_t percent)
374 {
375         value_t value;
376
377         value.gauge = percent;
378         submit_value (cpu_num, cpu_state, "percent", value);
379 }
380
381 static void submit_derive(int cpu_num, int cpu_state, derive_t derive)
382 {
383         value_t value;
384
385         value.derive = derive;
386         submit_value (cpu_num, cpu_state, "cpu", value);
387 }
388
389 static void submit_flush (void)
390 {
391         int i = 0;
392
393         if (report_by_cpu) {
394                 cpu_count = 0;
395                 return;
396         }
397
398         for (i = 0; i < CPU_SUBMIT_MAX; i++) {
399                 if (agg_percents[i] == -1)
400                         continue;
401
402                 submit_percent(-1, i, agg_percents[i] / cpu_count);
403                 agg_percents[i] = -1;
404         }
405         cpu_count = 0;
406 }
407
408 static void submit (int cpu_num, derive_t *derives)
409 {
410
411         int i = 0;
412
413         if (!report_percent && report_by_cpu) {
414                 derive_t cpu_active = 0;
415                 for (i = 0; i < CPU_SUBMIT_ACTIVE; i++)
416                 {
417                         if (derives[i] == -1)
418                                 continue;
419
420                         if (i != CPU_SUBMIT_IDLE)
421                                 cpu_active += derives[i];
422
423                         submit_derive(cpu_num, i, derives[i]);
424                 }
425                 if (report_active)
426                         submit_derive(cpu_num, CPU_SUBMIT_ACTIVE, cpu_active);
427         }
428         else /* we are reporting percents */
429         {
430                 cdtime_t cdt;
431                 gauge_t percent;
432                 gauge_t cpu_total = 0;
433                 gauge_t cpu_active = 0;
434                 gauge_t local_rates[CPU_SUBMIT_MAX];
435
436                 cpu_count++;
437                 if (cpu_states_grow())
438                         return;
439
440                 memset(local_rates, 0, sizeof(local_rates));
441
442                 cdt = cdtime();
443                 for (i = 0; i < CPU_SUBMIT_ACTIVE; i++) {
444                         value_t rate;
445                         int index;
446
447                         if (derives[i] == -1)
448                                 continue;
449
450                         index = (cpu_num * CPU_SUBMIT_MAX) + i;
451                         if (value_to_rate(&rate, derives[i], &percents[index],
452                                           DS_TYPE_DERIVE, cdt) != 0) {
453                                 local_rates[i] = -1;
454                                 continue;
455                         }
456
457                         local_rates[i] = rate.gauge;
458                         cpu_total += rate.gauge;
459                         if (i != CPU_SUBMIT_IDLE)
460                                 cpu_active += rate.gauge;
461                 }
462                 if (cpu_total == 0.0)
463                         return;
464
465                 if (report_active)
466                         local_rates[CPU_SUBMIT_ACTIVE] = cpu_active;
467
468                 for (i = 0; i < CPU_SUBMIT_MAX; i++) {
469                         if (local_rates[i] == -1)
470                                 continue;
471
472                         percent = (local_rates[i] / cpu_total) * 100;
473                         if (report_by_cpu)
474                                 submit_percent (cpu_num, i, percent);
475                         else {
476                                 if (agg_percents[i] == -1)
477                                         agg_percents[i] = percent;
478                                 else
479                                         agg_percents[i] += percent;
480                         }
481
482                 }
483         }
484 }
485
486 static int cpu_read (void)
487 {
488 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
489         int cpu;
490
491         kern_return_t status;
492
493 #if PROCESSOR_CPU_LOAD_INFO
494         processor_cpu_load_info_data_t cpu_info;
495         mach_msg_type_number_t         cpu_info_len;
496 #endif
497 #if PROCESSOR_TEMPERATURE
498         processor_info_data_t          cpu_temp;
499         mach_msg_type_number_t         cpu_temp_len;
500 #endif
501
502         host_t cpu_host;
503
504         for (cpu = 0; cpu < cpu_list_len; cpu++)
505         {
506 #if PROCESSOR_CPU_LOAD_INFO
507                 derive_t derives[CPU_SUBMIT_MAX] = {
508                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
509                 };
510                 memset(derives, -1, sizeof(derives));
511                 cpu_host = 0;
512                 cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
513
514                 if ((status = processor_info (cpu_list[cpu],
515                                                 PROCESSOR_CPU_LOAD_INFO, &cpu_host,
516                                                 (processor_info_t) &cpu_info, &cpu_info_len)) != KERN_SUCCESS)
517                 {
518                         ERROR ("cpu plugin: processor_info failed with status %i", (int) status);
519                         continue;
520                 }
521
522                 if (cpu_info_len < CPU_STATE_MAX)
523                 {
524                         ERROR ("cpu plugin: processor_info returned only %i elements..", cpu_info_len);
525                         continue;
526                 }
527
528                 derives[CPU_SUBMIT_USER] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER];
529                 derives[CPU_SUBMIT_NICE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE];
530                 derives[CPU_SUBMIT_SYSTEM] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
531                 derives[CPU_SUBMIT_IDLE] = (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE];
532                 submit (cpu, derives);
533
534 #endif /* PROCESSOR_CPU_LOAD_INFO */
535 #if PROCESSOR_TEMPERATURE
536                 /*
537                  * Not all Apple computers do have this ability. To minimize
538                  * the messages sent to the syslog we do an exponential
539                  * stepback if `processor_info' fails. We still try ~once a day
540                  * though..
541                  */
542                 if (cpu_temp_retry_counter > 0)
543                 {
544                         cpu_temp_retry_counter--;
545                         continue;
546                 }
547
548                 cpu_temp_len = PROCESSOR_INFO_MAX;
549
550                 status = processor_info (cpu_list[cpu],
551                                 PROCESSOR_TEMPERATURE,
552                                 &cpu_host,
553                                 cpu_temp, &cpu_temp_len);
554                 if (status != KERN_SUCCESS)
555                 {
556                         ERROR ("cpu plugin: processor_info failed: %s",
557                                         mach_error_string (status));
558
559                         cpu_temp_retry_counter = cpu_temp_retry_step;
560                         cpu_temp_retry_step *= 2;
561                         if (cpu_temp_retry_step > cpu_temp_retry_max)
562                                 cpu_temp_retry_step = cpu_temp_retry_max;
563
564                         continue;
565                 }
566
567                 if (cpu_temp_len != 1)
568                 {
569                         DEBUG ("processor_info (PROCESSOR_TEMPERATURE) returned %i elements..?",
570                                         (int) cpu_temp_len);
571                         continue;
572                 }
573
574                 cpu_temp_retry_counter = 0;
575                 cpu_temp_retry_step    = 1;
576 #endif /* PROCESSOR_TEMPERATURE */
577         }
578         submit_flush ();
579 /* #endif PROCESSOR_CPU_LOAD_INFO */
580
581 #elif defined(KERNEL_LINUX)
582         int cpu;
583         FILE *fh;
584         char buf[1024];
585
586         char *fields[9];
587         int numfields;
588
589         if ((fh = fopen ("/proc/stat", "r")) == NULL)
590         {
591                 char errbuf[1024];
592                 ERROR ("cpu plugin: fopen (/proc/stat) failed: %s",
593                                 sstrerror (errno, errbuf, sizeof (errbuf)));
594                 return (-1);
595         }
596
597         while (fgets (buf, 1024, fh) != NULL)
598         {
599                 derive_t derives[CPU_SUBMIT_MAX] = {
600                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
601                 };
602
603                 if (strncmp (buf, "cpu", 3))
604                         continue;
605                 if ((buf[3] < '0') || (buf[3] > '9'))
606                         continue;
607
608                 numfields = strsplit (buf, fields, 9);
609                 if (numfields < 5)
610                         continue;
611
612                 cpu = atoi (fields[0] + 3);
613                 derives[CPU_SUBMIT_USER] = atoll(fields[1]);
614                 derives[CPU_SUBMIT_NICE] = atoll(fields[2]);
615                 derives[CPU_SUBMIT_SYSTEM] = atoll(fields[3]);
616                 derives[CPU_SUBMIT_IDLE] = atoll(fields[4]);
617
618                 if (numfields >= 8)
619                 {
620                         derives[CPU_SUBMIT_WAIT] = atoll(fields[5]);
621                         derives[CPU_SUBMIT_INTERRUPT] = atoll(fields[6]);
622                         derives[CPU_SUBMIT_SOFTIRQ] = atoll(fields[6]);
623
624                         if (numfields >= 9)
625                                 derives[CPU_SUBMIT_STEAL] = atoll(fields[8]);
626                 }
627                 submit(cpu, derives);
628         }
629         submit_flush();
630
631         fclose (fh);
632 /* #endif defined(KERNEL_LINUX) */
633
634 #elif defined(HAVE_LIBKSTAT)
635         int cpu;
636         static cpu_stat_t cs;
637
638         if (kc == NULL)
639                 return (-1);
640
641         for (cpu = 0; cpu < numcpu; cpu++)
642         {
643                 derive_t derives[CPU_SUBMIT_MAX] = {
644                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
645                 };
646
647                 if (kstat_read (kc, ksp[cpu], &cs) == -1)
648                         continue; /* error message? */
649
650                 memset(derives, -1, sizeof(derives));
651                 derives[CPU_SUBMIT_IDLE] = cs.cpu_sysinfo.cpu[CPU_IDLE];
652                 derives[CPU_SUBMIT_USER] = cs.cpu_sysinfo.cpu[CPU_USER];
653                 derives[CPU_SUBMIT_SYSTEM] = cs.cpu_sysinfo.cpu[CPU_KERNEL];
654                 derives[CPU_SUBMIT_WAIT] = cs.cpu_sysinfo.cpu[CPU_WAIT];
655                 submit (ksp[cpu]->ks_instance, derives);
656         }
657         submit_flush ();
658 /* #endif defined(HAVE_LIBKSTAT) */
659
660 #elif CAN_USE_SYSCTL
661         uint64_t cpuinfo[numcpu][CPUSTATES];
662         size_t cpuinfo_size;
663         int status;
664         int i;
665
666         if (numcpu < 1)
667         {
668                 ERROR ("cpu plugin: Could not determine number of "
669                                 "installed CPUs using sysctl(3).");
670                 return (-1);
671         }
672
673         memset (cpuinfo, 0, sizeof (cpuinfo));
674
675 #if defined(KERN_CPTIME2)
676         if (numcpu > 1) {
677                 for (i = 0; i < numcpu; i++) {
678                         int mib[] = {CTL_KERN, KERN_CPTIME2, i};
679
680                         cpuinfo_size = sizeof (cpuinfo[0]);
681
682                         status = sysctl (mib, STATIC_ARRAY_SIZE (mib),
683                                         cpuinfo[i], &cpuinfo_size, NULL, 0);
684                         if (status == -1) {
685                                 char errbuf[1024];
686                                 ERROR ("cpu plugin: sysctl failed: %s.",
687                                                 sstrerror (errno, errbuf, sizeof (errbuf)));
688                                 return (-1);
689                         }
690                 }
691         }
692         else
693 #endif /* defined(KERN_CPTIME2) */
694         {
695                 int mib[] = {CTL_KERN, KERN_CPTIME};
696                 long cpuinfo_tmp[CPUSTATES];
697
698                 cpuinfo_size = sizeof(cpuinfo_tmp);
699
700                 status = sysctl (mib, STATIC_ARRAY_SIZE (mib),
701                                         &cpuinfo_tmp, &cpuinfo_size, NULL, 0);
702                 if (status == -1)
703                 {
704                         char errbuf[1024];
705                         ERROR ("cpu plugin: sysctl failed: %s.",
706                                         sstrerror (errno, errbuf, sizeof (errbuf)));
707                         return (-1);
708                 }
709
710                 for(i = 0; i < CPUSTATES; i++) {
711                         cpuinfo[0][i] = cpuinfo_tmp[i];
712                 }
713         }
714
715         for (i = 0; i < numcpu; i++) {
716                 derive_t derives[CPU_SUBMIT_MAX] = {
717                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
718                 };
719
720                 derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
721                 derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
722                 derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
723                 derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
724                 derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
725                 submit(i, derives);
726         }
727         submit_flush();
728 /* #endif CAN_USE_SYSCTL */
729 #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
730         long cpuinfo[maxcpu][CPUSTATES];
731         size_t cpuinfo_size;
732         int i;
733
734         memset (cpuinfo, 0, sizeof (cpuinfo));
735
736         cpuinfo_size = sizeof (cpuinfo);
737         if (sysctlbyname("kern.cp_times", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
738         {
739                 char errbuf[1024];
740                 ERROR ("cpu plugin: sysctlbyname failed: %s.",
741                                 sstrerror (errno, errbuf, sizeof (errbuf)));
742                 return (-1);
743         }
744
745         for (i = 0; i < numcpu; i++) {
746                 derive_t derives[CPU_SUBMIT_MAX] = {
747                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
748                 };
749
750                 derives[CPU_SUBMIT_USER] = cpuinfo[i][CP_USER];
751                 derives[CPU_SUBMIT_NICE] = cpuinfo[i][CP_NICE];
752                 derives[CPU_SUBMIT_SYSTEM] = cpuinfo[i][CP_SYS];
753                 derives[CPU_SUBMIT_IDLE] = cpuinfo[i][CP_IDLE];
754                 derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[i][CP_INTR];
755                 submit(i, derives);
756         }
757         submit_flush();
758
759 /* #endif HAVE_SYSCTL_KERN_CP_TIMES */
760 #elif defined(HAVE_SYSCTLBYNAME)
761         long cpuinfo[CPUSTATES];
762         size_t cpuinfo_size;
763         derive_t derives[CPU_SUBMIT_MAX] = {
764                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
765         };
766
767         cpuinfo_size = sizeof (cpuinfo);
768
769         if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
770         {
771                 char errbuf[1024];
772                 ERROR ("cpu plugin: sysctlbyname failed: %s.",
773                                 sstrerror (errno, errbuf, sizeof (errbuf)));
774                 return (-1);
775         }
776
777         derives[CPU_SUBMIT_USER] = cpuinfo[CP_USER];
778         derives[CPU_SUBMIT_SYSTEM] = cpuinfo[CP_SYS];
779         derives[CPU_SUBMIT_NICE] = cpuinfo[CP_NICE];
780         derives[CPU_SUBMIT_IDLE] = cpuinfo[CP_IDLE];
781         derives[CPU_SUBMIT_INTERRUPT] = cpuinfo[CP_INTR];
782         submit(0, derives);
783         submit_flush();
784
785 /* #endif HAVE_SYSCTLBYNAME */
786
787 #elif defined(HAVE_LIBSTATGRAB)
788         sg_cpu_stats *cs;
789         derive_t derives[CPU_SUBMIT_MAX] = {
790                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
791         };
792         cs = sg_get_cpu_stats ();
793
794         if (cs == NULL)
795         {
796                 ERROR ("cpu plugin: sg_get_cpu_stats failed.");
797                 return (-1);
798         }
799
800         derives[CPU_SUBMIT_IDLE] = (derive_t) cs->idle;
801         derives[CPU_SUBMIT_NICE] = (derive_t) cs->nice;
802         derives[CPU_SUBMIT_SWAP] = (derive_t) cs->swap;
803         derives[CPU_SUBMIT_SYSTEM] = (derive_t) cs->kernel;
804         derives[CPU_SUBMIT_USER] = (derive_t) cs->user;
805         derives[CPU_SUBMIT_WAIT] = (derive_t) cs->iowait;
806         submit(0, derives);
807         submit_flush();
808 /* #endif HAVE_LIBSTATGRAB */
809
810 #elif defined(HAVE_PERFSTAT)
811         perfstat_id_t id;
812         int i, cpus;
813
814         numcpu =  perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
815         if(numcpu == -1)
816         {
817                 char errbuf[1024];
818                 WARNING ("cpu plugin: perfstat_cpu: %s",
819                         sstrerror (errno, errbuf, sizeof (errbuf)));
820                 return (-1);
821         }
822
823         if (pnumcpu != numcpu || perfcpu == NULL)
824         {
825                 if (perfcpu != NULL)
826                         free(perfcpu);
827                 perfcpu = malloc(numcpu * sizeof(perfstat_cpu_t));
828         }
829         pnumcpu = numcpu;
830
831         id.name[0] = '\0';
832         if ((cpus = perfstat_cpu(&id, perfcpu, sizeof(perfstat_cpu_t), numcpu)) < 0)
833         {
834                 char errbuf[1024];
835                 WARNING ("cpu plugin: perfstat_cpu: %s",
836                         sstrerror (errno, errbuf, sizeof (errbuf)));
837                 return (-1);
838         }
839
840         for (i = 0; i < cpus; i++)
841         {
842                 derive_t derives[CPU_SUBMIT_MAX] = {
843                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
844                 };
845                 derives[CPU_SUBMIT_IDLE] = perfcpu[i].idle;
846                 derives[CPU_SUBMIT_SYSTEM] = perfcpu[i].sys;
847                 derives[CPU_SUBMIT_USER] = perfcpu[i].user;
848                 derives[CPU_SUBMIT_WAIT] = perfcpu[i].wait;
849                 submit(i, derives);
850         }
851         submit_flush();
852 #endif /* HAVE_PERFSTAT */
853
854         return (0);
855 }
856
857 void module_register (void)
858 {
859         plugin_register_init ("cpu", init);
860         plugin_register_config ("cpu", cpu_config, config_keys, config_keys_num);
861         plugin_register_read ("cpu", cpu_read);
862 } /* void module_register */