1 /**
2  * collectd - src/cpu.c
3  * Copyright (C) 2005-2014  Florian octo Forster
4  * Copyright (C) 2008       Oleg King
5  * Copyright (C) 2009       Simon Kuhnle
6  * Copyright (C) 2009       Manuel Sanmartin
7  * Copyright (C) 2013-2014  Pierre-Yves Ritschard
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the
11  * Free Software Foundation; only version 2 of the License is applicable.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
21  *
22  * Authors:
23  *   Florian octo Forster <octo at collectd.org>
24  *   Oleg King <king2 at kaluga.ru>
25  *   Simon Kuhnle <simon at blarzwurst.de>
26  *   Manuel Sanmartin
27  *   Pierre-Yves Ritschard <pyr at spootnik.org>
28  **/
29 
30 #include "collectd.h"
31 
32 #include "plugin.h"
33 #include "utils/common/common.h"
34 
35 #ifdef HAVE_MACH_KERN_RETURN_H
36 #include <mach/kern_return.h>
37 #endif
38 #ifdef HAVE_MACH_MACH_INIT_H
39 #include <mach/mach_init.h>
40 #endif
41 #ifdef HAVE_MACH_HOST_PRIV_H
42 #include <mach/host_priv.h>
43 #endif
44 #if HAVE_MACH_MACH_ERROR_H
45 #include <mach/mach_error.h>
46 #endif
47 #ifdef HAVE_MACH_PROCESSOR_INFO_H
48 #include <mach/processor_info.h>
49 #endif
50 #ifdef HAVE_MACH_PROCESSOR_H
51 #include <mach/processor.h>
52 #endif
53 #ifdef HAVE_MACH_VM_MAP_H
54 #include <mach/vm_map.h>
55 #endif
56 #ifdef HAVE_SYS_PARAM_H
57 #include <sys/param.h>
58 #endif
59 
60 #ifdef __DragonFly__
61 #include <sys/resource.h>
62 #endif
63 
64 #ifdef HAVE_LIBKSTAT
65 #include <sys/sysinfo.h>
66 #endif /* HAVE_LIBKSTAT */
67 
68 #if (defined(HAVE_SYSCTL) && defined(HAVE_SYSCTLBYNAME)) || defined(__OpenBSD__)
69 /* Implies BSD variant */
70 #include <sys/sysctl.h>
71 #endif
72 
73 #ifdef HAVE_SYS_DKSTAT_H
74 /* implies BSD variant */
75 #include <sys/dkstat.h>
76 
77 #if !defined(CP_USER) || !defined(CP_NICE) || !defined(CP_SYS) ||              \
78     !defined(CP_INTR) || !defined(CP_IDLE) || !defined(CPUSTATES)
79 #define CP_USER 0
80 #define CP_NICE 1
81 #define CP_SYS 2
82 #define CP_INTR 3
83 #define CP_IDLE 4
84 #define CPUSTATES 5
85 #endif
86 #endif /* HAVE_SYS_DKSTAT_H */
87 
88 #if (defined(HAVE_SYSCTL) && defined(HAVE_SYSCTLBYNAME)) || defined(__OpenBSD__)
89 /* Implies BSD variant */
90 #if defined(CTL_HW) && defined(HW_NCPU) && defined(CTL_KERN) &&                \
91     (defined(KERN_CPTIME) || defined(KERN_CP_TIME)) && defined(CPUSTATES)
92 #define CAN_USE_SYSCTL 1
93 #else
94 #define CAN_USE_SYSCTL 0
95 #endif
96 #else
97 #define CAN_USE_SYSCTL 0
98 #endif /* HAVE_SYSCTL_H && HAVE_SYSCTLBYNAME || __OpenBSD__ */
99 
100 #define COLLECTD_CPU_STATE_USER 0
101 #define COLLECTD_CPU_STATE_SYSTEM 1
102 #define COLLECTD_CPU_STATE_WAIT 2
103 #define COLLECTD_CPU_STATE_NICE 3
104 #define COLLECTD_CPU_STATE_SWAP 4
105 #define COLLECTD_CPU_STATE_INTERRUPT 5
106 #define COLLECTD_CPU_STATE_SOFTIRQ 6
107 #define COLLECTD_CPU_STATE_STEAL 7
108 #define COLLECTD_CPU_STATE_GUEST 8
109 #define COLLECTD_CPU_STATE_GUEST_NICE 9
110 #define COLLECTD_CPU_STATE_IDLE 10
111 #define COLLECTD_CPU_STATE_ACTIVE 11 /* sum of (!idle) */
112 #define COLLECTD_CPU_STATE_MAX 12    /* #states */
113 
114 #if HAVE_STATGRAB_H
115 #include <statgrab.h>
116 #endif
117 
118 #ifdef HAVE_PERFSTAT
119 #include <libperfstat.h>
120 #include <sys/protosw.h>
121 #endif /* HAVE_PERFSTAT */
122 
123 #if !PROCESSOR_CPU_LOAD_INFO && !KERNEL_LINUX && !HAVE_LIBKSTAT &&             \
124     !CAN_USE_SYSCTL && !HAVE_SYSCTLBYNAME && !HAVE_LIBSTATGRAB &&              \
125     !HAVE_PERFSTAT
126 #error "No applicable input method."
127 #endif
128 
129 static const char *cpu_state_names[] = {
130     "user",    "system", "wait",  "nice",       "swap", "interrupt",
131     "softirq", "steal",  "guest", "guest_nice", "idle", "active"};
132 
133 #ifdef PROCESSOR_CPU_LOAD_INFO
134 static mach_port_t port_host;
135 static processor_port_array_t cpu_list;
136 static mach_msg_type_number_t cpu_list_len;
137 /* #endif PROCESSOR_CPU_LOAD_INFO */
138 
139 #elif defined(KERNEL_LINUX)
140 /* no variables needed */
141 /* #endif KERNEL_LINUX */
142 
143 #elif defined(HAVE_LIBKSTAT)
144 #if HAVE_KSTAT_H
145 #include <kstat.h>
146 #endif
147 /* colleague tells me that Sun doesn't sell systems with more than 100 or so
148  * CPUs.. */
149 #define MAX_NUMCPU 256
150 extern kstat_ctl_t *kc;
151 static kstat_t *ksp[MAX_NUMCPU];
152 static int numcpu;
153 /* #endif HAVE_LIBKSTAT */
154 
155 #elif CAN_USE_SYSCTL
156 /* Only possible for (Open) BSD variant */
157 static int numcpu;
158 /* #endif CAN_USE_SYSCTL */
159 
160 #elif defined(HAVE_SYSCTLBYNAME)
161 /* Implies BSD variant */
162 static int numcpu;
163 #ifdef HAVE_SYSCTL_KERN_CP_TIMES
164 static int maxcpu;
165 #endif /* HAVE_SYSCTL_KERN_CP_TIMES */
166 /* #endif HAVE_SYSCTLBYNAME */
167 
168 #elif defined(HAVE_LIBSTATGRAB)
169 /* no variables needed */
170 /* #endif  HAVE_LIBSTATGRAB */
171 
172 #elif defined(HAVE_PERFSTAT)
173 #define TOTAL_IDLE 0
174 #define TOTAL_USER 1
175 #define TOTAL_SYS 2
176 #define TOTAL_WAIT 3
177 #define TOTAL_STAT_NUM 4
178 static value_to_rate_state_t total_conv[TOTAL_STAT_NUM];
179 static perfstat_cpu_t *perfcpu;
180 static int numcpu;
181 static int pnumcpu;
182 #endif /* HAVE_PERFSTAT */
183 
184 #define RATE_ADD(sum, val)                                                     \
185   do {                                                                         \
186     if (isnan(sum))                                                            \
187       (sum) = (val);                                                           \
188     else if (!isnan(val))                                                      \
189       (sum) += (val);                                                          \
190   } while (0)
191 
192 struct cpu_state_s {
193   value_to_rate_state_t conv;
194   gauge_t rate;
195   bool has_value;
196 };
197 typedef struct cpu_state_s cpu_state_t;
198 
199 static cpu_state_t *cpu_states;
200 static size_t cpu_states_num; /* #cpu_states allocated */
201 
202 /* Highest CPU number in the current iteration. Used by the dispatch logic to
203  * determine how many CPUs there were. Reset to 0 by cpu_reset(). */
204 static size_t global_cpu_num;
205 
206 static bool report_by_cpu = true;
207 static bool report_by_state = true;
208 static bool report_percent;
209 static bool report_num_cpu;
210 static bool report_guest;
211 static bool subtract_guest = true;
212 
213 static const char *config_keys[] = {"ReportByCpu",      "ReportByState",
214                                     "ReportNumCpu",     "ValuesPercentage",
215                                     "ReportGuestState", "SubtractGuestState"};
216 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
217 
cpu_config(char const * key,char const * value)218 static int cpu_config(char const *key, char const *value) /* {{{ */
219 {
220   if (strcasecmp(key, "ReportByCpu") == 0)
221     report_by_cpu = IS_TRUE(value);
222   else if (strcasecmp(key, "ValuesPercentage") == 0)
223     report_percent = IS_TRUE(value);
224   else if (strcasecmp(key, "ReportByState") == 0)
225     report_by_state = IS_TRUE(value);
226   else if (strcasecmp(key, "ReportNumCpu") == 0)
227     report_num_cpu = IS_TRUE(value);
228   else if (strcasecmp(key, "ReportGuestState") == 0)
229     report_guest = IS_TRUE(value);
230   else if (strcasecmp(key, "SubtractGuestState") == 0)
231     subtract_guest = IS_TRUE(value);
232   else
233     return -1;
234 
235   return 0;
236 } /* }}} int cpu_config */
237 
init(void)238 static int init(void) {
239 #if PROCESSOR_CPU_LOAD_INFO
240   kern_return_t status;
241 
242   port_host = mach_host_self();
243 
244   status = host_processors(port_host, &cpu_list, &cpu_list_len);
245   if (status == KERN_INVALID_ARGUMENT) {
246     ERROR("cpu plugin: Don't have a privileged host control port. "
247           "The most common cause for this problem is "
248           "that collectd is running without root "
249           "privileges, which are required to read CPU "
250           "load information. "
251           "<https://collectd.org/bugs/22>");
252     cpu_list_len = 0;
253     return -1;
254   }
255   if (status != KERN_SUCCESS) {
256     ERROR("cpu plugin: host_processors() failed with status %d.", (int)status);
257     cpu_list_len = 0;
258     return -1;
259   }
260 
261   INFO("cpu plugin: Found %i processor%s.", (int)cpu_list_len,
262        cpu_list_len == 1 ? "" : "s");
263   /* #endif PROCESSOR_CPU_LOAD_INFO */
264 
265 #elif defined(HAVE_LIBKSTAT)
266   kstat_t *ksp_chain;
267 
268   numcpu = 0;
269 
270   if (kc == NULL)
271     return -1;
272 
273   /* Solaris doesn't count linear.. *sigh* */
274   for (numcpu = 0, ksp_chain = kc->kc_chain;
275        (numcpu < MAX_NUMCPU) && (ksp_chain != NULL);
276        ksp_chain = ksp_chain->ks_next)
277     if (strncmp(ksp_chain->ks_module, "cpu_stat", 8) == 0)
278       ksp[numcpu++] = ksp_chain;
279       /* #endif HAVE_LIBKSTAT */
280 
281 #elif CAN_USE_SYSCTL
282   /* Only on (Open) BSD variant */
283   size_t numcpu_size;
284   int mib[2] = {CTL_HW, HW_NCPU};
285   int status;
286 
287   numcpu = 0;
288   numcpu_size = sizeof(numcpu);
289 
290   status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &numcpu, &numcpu_size, NULL, 0);
291   if (status == -1) {
292     WARNING("cpu plugin: sysctl: %s", STRERRNO);
293     return -1;
294   }
295   /* #endif CAN_USE_SYSCTL */
296 
297 #elif defined(HAVE_SYSCTLBYNAME)
298   /* Only on BSD varient */
299   size_t numcpu_size;
300 
301   numcpu_size = sizeof(numcpu);
302 
303   if (sysctlbyname("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0) {
304     WARNING("cpu plugin: sysctlbyname(hw.ncpu): %s", STRERRNO);
305     return -1;
306   }
307 
308 #ifdef HAVE_SYSCTL_KERN_CP_TIMES
309   numcpu_size = sizeof(maxcpu);
310 
311   if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &numcpu_size, NULL, 0) < 0) {
312     /*
313      * DragonFly BSD defines SMP_MAXCPU via sys/param.h (and FreeBSD doesn't).
314      * This #ifdef will pick up the case until that sysctl is available.
315      */
316 #ifdef SMP_MAXCPU
317     if (maxcpu == 0)
318       maxcpu = SMP_MAXCPU;
319 #else
320     WARNING("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s", STRERRNO);
321     return -1;
322 #endif
323   }
324 #else
325   if (numcpu != 1)
326     NOTICE("cpu: Only one processor supported when using `sysctlbyname' (found "
327            "%i)",
328            numcpu);
329 #endif
330   /* #endif HAVE_SYSCTLBYNAME */
331 
332 #elif defined(HAVE_LIBSTATGRAB)
333   /* nothing to initialize */
334   /* #endif HAVE_LIBSTATGRAB */
335 
336 #elif defined(HAVE_PERFSTAT)
337 /* nothing to initialize */
338 #endif /* HAVE_PERFSTAT */
339 
340   return 0;
341 } /* int init */
342 
submit_value(int cpu_num,int cpu_state,const char * type,value_t value)343 static void submit_value(int cpu_num, int cpu_state, const char *type,
344                          value_t value) {
345   value_list_t vl = VALUE_LIST_INIT;
346 
347   vl.values = &value;
348   vl.values_len = 1;
349 
350   sstrncpy(vl.plugin, "cpu", sizeof(vl.plugin));
351   sstrncpy(vl.type, type, sizeof(vl.type));
352   sstrncpy(vl.type_instance, cpu_state_names[cpu_state],
353            sizeof(vl.type_instance));
354 
355   if (cpu_num >= 0) {
356     snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num);
357   }
358   plugin_dispatch_values(&vl);
359 }
360 
submit_percent(int cpu_num,int cpu_state,gauge_t value)361 static void submit_percent(int cpu_num, int cpu_state, gauge_t value) {
362   /* This function is called for all known CPU states, but each read
363    * method will only report a subset. The remaining states are left as
364    * NAN and we ignore them here. */
365   if (isnan(value))
366     return;
367 
368   submit_value(cpu_num, cpu_state, "percent", (value_t){.gauge = value});
369 }
370 
submit_derive(int cpu_num,int cpu_state,derive_t value)371 static void submit_derive(int cpu_num, int cpu_state, derive_t value) {
372   submit_value(cpu_num, cpu_state, "cpu", (value_t){.derive = value});
373 }
374 
375 /* Takes the zero-index number of a CPU and makes sure that the module-global
376  * cpu_states buffer is large enough. Returne ENOMEM on erorr. */
cpu_states_alloc(size_t cpu_num)377 static int cpu_states_alloc(size_t cpu_num) /* {{{ */
378 {
379   cpu_state_t *tmp;
380   size_t sz;
381 
382   sz = (((size_t)cpu_num) + 1) * COLLECTD_CPU_STATE_MAX;
383   assert(sz > 0);
384 
385   /* We already have enough space. */
386   if (cpu_states_num >= sz)
387     return 0;
388 
389   tmp = realloc(cpu_states, sz * sizeof(*cpu_states));
390   if (tmp == NULL) {
391     ERROR("cpu plugin: realloc failed.");
392     return ENOMEM;
393   }
394   cpu_states = tmp;
395   tmp = cpu_states + cpu_states_num;
396 
397   memset(tmp, 0, (sz - cpu_states_num) * sizeof(*cpu_states));
398   cpu_states_num = sz;
399   return 0;
400 } /* }}} cpu_states_alloc */
401 
get_cpu_state(size_t cpu_num,size_t state)402 static cpu_state_t *get_cpu_state(size_t cpu_num, size_t state) /* {{{ */
403 {
404   size_t index = ((cpu_num * COLLECTD_CPU_STATE_MAX) + state);
405 
406   if (index >= cpu_states_num)
407     return NULL;
408 
409   return &cpu_states[index];
410 } /* }}} cpu_state_t *get_cpu_state */
411 
412 #if defined(HAVE_PERFSTAT) /* {{{ */
413 /* populate global aggregate cpu rate */
total_rate(gauge_t * sum_by_state,size_t state,derive_t d,value_to_rate_state_t * conv,cdtime_t now)414 static int total_rate(gauge_t *sum_by_state, size_t state, derive_t d,
415                       value_to_rate_state_t *conv, cdtime_t now) {
416   gauge_t rate = NAN;
417   int status =
418       value_to_rate(&rate, (value_t){.derive = d}, DS_TYPE_DERIVE, now, conv);
419   if (status != 0)
420     return status;
421 
422   sum_by_state[state] = rate;
423 
424   if (state != COLLECTD_CPU_STATE_IDLE)
425     RATE_ADD(sum_by_state[COLLECTD_CPU_STATE_ACTIVE], sum_by_state[state]);
426   return 0;
427 }
428 #endif /* }}} HAVE_PERFSTAT */
429 
430 /* Populates the per-CPU COLLECTD_CPU_STATE_ACTIVE rate and the global
431  * rate_by_state
432  * array. */
aggregate(gauge_t * sum_by_state)433 static void aggregate(gauge_t *sum_by_state) /* {{{ */
434 {
435   for (size_t state = 0; state < COLLECTD_CPU_STATE_MAX; state++)
436     sum_by_state[state] = NAN;
437 
438   for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
439     cpu_state_t *this_cpu_states = get_cpu_state(cpu_num, 0);
440 
441     this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate = NAN;
442 
443     for (size_t state = 0; state < COLLECTD_CPU_STATE_ACTIVE; state++) {
444       if (!this_cpu_states[state].has_value)
445         continue;
446 
447       RATE_ADD(sum_by_state[state], this_cpu_states[state].rate);
448       if (state != COLLECTD_CPU_STATE_IDLE)
449         RATE_ADD(this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate,
450                  this_cpu_states[state].rate);
451     }
452 
453     if (!isnan(this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate))
454       this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].has_value = true;
455 
456     RATE_ADD(sum_by_state[COLLECTD_CPU_STATE_ACTIVE],
457              this_cpu_states[COLLECTD_CPU_STATE_ACTIVE].rate);
458   }
459 
460 #if defined(HAVE_PERFSTAT) /* {{{ */
461   cdtime_t now = cdtime();
462   perfstat_cpu_total_t cputotal = {0};
463 
464   if (!perfstat_cpu_total(NULL, &cputotal, sizeof(cputotal), 1)) {
465     WARNING("cpu plugin: perfstat_cpu_total: %s", STRERRNO);
466     return;
467   }
468 
469   /* Reset COLLECTD_CPU_STATE_ACTIVE */
470   sum_by_state[COLLECTD_CPU_STATE_ACTIVE] = NAN;
471 
472   /* Physical Processor Utilization */
473   total_rate(sum_by_state, COLLECTD_CPU_STATE_IDLE, (derive_t)cputotal.pidle,
474              &total_conv[TOTAL_IDLE], now);
475   total_rate(sum_by_state, COLLECTD_CPU_STATE_USER, (derive_t)cputotal.puser,
476              &total_conv[TOTAL_USER], now);
477   total_rate(sum_by_state, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cputotal.psys,
478              &total_conv[TOTAL_SYS], now);
479   total_rate(sum_by_state, COLLECTD_CPU_STATE_WAIT, (derive_t)cputotal.pwait,
480              &total_conv[TOTAL_WAIT], now);
481 #endif /* }}} HAVE_PERFSTAT */
482 } /* }}} void aggregate */
483 
484 /* Commits (dispatches) the values for one CPU or the global aggregation.
485  * cpu_num is the index of the CPU to be committed or -1 in case of the global
486  * aggregation. rates is a pointer to COLLECTD_CPU_STATE_MAX gauge_t values
487  * holding the
488  * current rate; each rate may be NAN. Calculates the percentage of each state
489  * and dispatches the metric. */
cpu_commit_one(int cpu_num,gauge_t rates[static COLLECTD_CPU_STATE_MAX])490 static void cpu_commit_one(int cpu_num, /* {{{ */
491                            gauge_t rates[static COLLECTD_CPU_STATE_MAX]) {
492   gauge_t sum;
493 
494   sum = rates[COLLECTD_CPU_STATE_ACTIVE];
495   RATE_ADD(sum, rates[COLLECTD_CPU_STATE_IDLE]);
496 
497   if (!report_by_state) {
498     gauge_t percent = 100.0 * rates[COLLECTD_CPU_STATE_ACTIVE] / sum;
499     submit_percent(cpu_num, COLLECTD_CPU_STATE_ACTIVE, percent);
500     return;
501   }
502 
503   for (size_t state = 0; state < COLLECTD_CPU_STATE_ACTIVE; state++) {
504     gauge_t percent = 100.0 * rates[state] / sum;
505     submit_percent(cpu_num, state, percent);
506   }
507 } /* }}} void cpu_commit_one */
508 
509 /* Commits the number of cores */
cpu_commit_num_cpu(gauge_t value)510 static void cpu_commit_num_cpu(gauge_t value) /* {{{ */
511 {
512   value_list_t vl = VALUE_LIST_INIT;
513 
514   vl.values = &(value_t){.gauge = value};
515   vl.values_len = 1;
516 
517   sstrncpy(vl.plugin, "cpu", sizeof(vl.plugin));
518   sstrncpy(vl.type, "count", sizeof(vl.type));
519 
520   plugin_dispatch_values(&vl);
521 } /* }}} void cpu_commit_num_cpu */
522 
523 /* Resets the internal aggregation. This is called by the read callback after
524  * each iteration / after each call to cpu_commit(). */
cpu_reset(void)525 static void cpu_reset(void) /* {{{ */
526 {
527   for (size_t i = 0; i < cpu_states_num; i++)
528     cpu_states[i].has_value = false;
529 
530   global_cpu_num = 0;
531 } /* }}} void cpu_reset */
532 
533 /* Legacy behavior: Dispatches the raw derive values without any aggregation. */
cpu_commit_without_aggregation(void)534 static void cpu_commit_without_aggregation(void) /* {{{ */
535 {
536   for (int state = 0; state < COLLECTD_CPU_STATE_ACTIVE; state++) {
537     for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
538       cpu_state_t *s = get_cpu_state(cpu_num, state);
539 
540       if (!s->has_value)
541         continue;
542 
543       submit_derive((int)cpu_num, (int)state, s->conv.last_value.derive);
544     }
545   }
546 } /* }}} void cpu_commit_without_aggregation */
547 
548 /* Aggregates the internal state and dispatches the metrics. */
cpu_commit(void)549 static void cpu_commit(void) /* {{{ */
550 {
551   gauge_t global_rates[COLLECTD_CPU_STATE_MAX] = {
552       NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
553   };
554 
555   if (report_num_cpu)
556     cpu_commit_num_cpu((gauge_t)global_cpu_num);
557 
558   if (report_by_state && report_by_cpu && !report_percent) {
559     cpu_commit_without_aggregation();
560     return;
561   }
562 
563   aggregate(global_rates);
564 
565   if (!report_by_cpu) {
566     cpu_commit_one(-1, global_rates);
567     return;
568   }
569 
570   for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
571     cpu_state_t *this_cpu_states = get_cpu_state(cpu_num, 0);
572     gauge_t local_rates[COLLECTD_CPU_STATE_MAX] = {
573         NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN};
574 
575     for (size_t state = 0; state < COLLECTD_CPU_STATE_MAX; state++)
576       if (this_cpu_states[state].has_value)
577         local_rates[state] = this_cpu_states[state].rate;
578 
579     cpu_commit_one((int)cpu_num, local_rates);
580   }
581 } /* }}} void cpu_commit */
582 
583 /* Adds a derive value to the internal state. This should be used by each read
584  * function for each state. At the end of the iteration, the read function
585  * should call cpu_commit(). */
cpu_stage(size_t cpu_num,size_t state,derive_t d,cdtime_t now)586 static int cpu_stage(size_t cpu_num, size_t state, derive_t d,
587                      cdtime_t now) /* {{{ */
588 {
589   int status;
590   cpu_state_t *s;
591   gauge_t rate = NAN;
592   value_t val = {.derive = d};
593 
594   if (state >= COLLECTD_CPU_STATE_ACTIVE)
595     return EINVAL;
596 
597   status = cpu_states_alloc(cpu_num);
598   if (status != 0)
599     return status;
600 
601   if (global_cpu_num <= cpu_num)
602     global_cpu_num = cpu_num + 1;
603 
604   s = get_cpu_state(cpu_num, state);
605 
606   status = value_to_rate(&rate, val, DS_TYPE_DERIVE, now, &s->conv);
607   if (status != 0)
608     return status;
609 
610   s->rate = rate;
611   s->has_value = true;
612   return 0;
613 } /* }}} int cpu_stage */
614 
cpu_read(void)615 static int cpu_read(void) {
616   cdtime_t now = cdtime();
617 
618 #if PROCESSOR_CPU_LOAD_INFO /* {{{ */
619   kern_return_t status;
620 
621   processor_cpu_load_info_data_t cpu_info;
622   mach_msg_type_number_t cpu_info_len;
623 
624   host_t cpu_host;
625 
626   for (mach_msg_type_number_t cpu = 0; cpu < cpu_list_len; cpu++) {
627     cpu_host = 0;
628     cpu_info_len = PROCESSOR_BASIC_INFO_COUNT;
629 
630     status = processor_info(cpu_list[cpu], PROCESSOR_CPU_LOAD_INFO, &cpu_host,
631                             (processor_info_t)&cpu_info, &cpu_info_len);
632     if (status != KERN_SUCCESS) {
633       ERROR("cpu plugin: processor_info (PROCESSOR_CPU_LOAD_INFO) failed: %s",
634             mach_error_string(status));
635       continue;
636     }
637 
638     if (cpu_info_len < CPU_STATE_MAX) {
639       ERROR("cpu plugin: processor_info returned only %i elements..",
640             cpu_info_len);
641       continue;
642     }
643 
644     cpu_stage(cpu, COLLECTD_CPU_STATE_USER,
645               (derive_t)cpu_info.cpu_ticks[CPU_STATE_USER], now);
646     cpu_stage(cpu, COLLECTD_CPU_STATE_NICE,
647               (derive_t)cpu_info.cpu_ticks[CPU_STATE_NICE], now);
648     cpu_stage(cpu, COLLECTD_CPU_STATE_SYSTEM,
649               (derive_t)cpu_info.cpu_ticks[CPU_STATE_SYSTEM], now);
650     cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE,
651               (derive_t)cpu_info.cpu_ticks[CPU_STATE_IDLE], now);
652   }
653   /* }}} #endif PROCESSOR_CPU_LOAD_INFO */
654 
655 #elif defined(KERNEL_LINUX) /* {{{ */
656   int cpu;
657   FILE *fh;
658   char buf[1024];
659 
660   char *fields[11];
661   int numfields;
662 
663   if ((fh = fopen("/proc/stat", "r")) == NULL) {
664     ERROR("cpu plugin: fopen (/proc/stat) failed: %s", STRERRNO);
665     return -1;
666   }
667 
668   while (fgets(buf, 1024, fh) != NULL) {
669     if (strncmp(buf, "cpu", 3))
670       continue;
671     if ((buf[3] < '0') || (buf[3] > '9'))
672       continue;
673 
674     numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE(fields));
675     if (numfields < 5)
676       continue;
677 
678     cpu = atoi(fields[0] + 3);
679 
680     /* Do not stage User and Nice immediately: we may need to alter them later:
681      */
682     long long user_value = atoll(fields[1]);
683     long long nice_value = atoll(fields[2]);
684     cpu_stage(cpu, COLLECTD_CPU_STATE_SYSTEM, (derive_t)atoll(fields[3]), now);
685     cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE, (derive_t)atoll(fields[4]), now);
686 
687     if (numfields >= 8) {
688       cpu_stage(cpu, COLLECTD_CPU_STATE_WAIT, (derive_t)atoll(fields[5]), now);
689       cpu_stage(cpu, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)atoll(fields[6]),
690                 now);
691       cpu_stage(cpu, COLLECTD_CPU_STATE_SOFTIRQ, (derive_t)atoll(fields[7]),
692                 now);
693     }
694 
695     if (numfields >= 9) { /* Steal (since Linux 2.6.11) */
696       cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]), now);
697     }
698 
699     if (numfields >= 10) { /* Guest (since Linux 2.6.24) */
700       if (report_guest) {
701         long long value = atoll(fields[9]);
702         cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST, (derive_t)value, now);
703         /* Guest is included in User; optionally subtract Guest from User: */
704         if (subtract_guest) {
705           user_value -= value;
706           if (user_value < 0)
707             user_value = 0;
708         }
709       }
710     }
711 
712     if (numfields >= 11) { /* Guest_nice (since Linux 2.6.33) */
713       if (report_guest) {
714         long long value = atoll(fields[10]);
715         cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST_NICE, (derive_t)value, now);
716         /* Guest_nice is included in Nice; optionally subtract Guest_nice from
717            Nice: */
718         if (subtract_guest) {
719           nice_value -= value;
720           if (nice_value < 0)
721             nice_value = 0;
722         }
723       }
724     }
725 
726     /* Eventually stage User and Nice: */
727     cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)user_value, now);
728     cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)nice_value, now);
729   }
730   fclose(fh);
731   /* }}} #endif defined(KERNEL_LINUX) */
732 
733 #elif defined(HAVE_LIBKSTAT) /* {{{ */
734   static cpu_stat_t cs;
735 
736   if (kc == NULL)
737     return -1;
738 
739   for (int cpu = 0; cpu < numcpu; cpu++) {
740     if (kstat_read(kc, ksp[cpu], &cs) == -1)
741       continue; /* error message? */
742 
743     cpu_stage(ksp[cpu]->ks_instance, COLLECTD_CPU_STATE_IDLE,
744               (derive_t)cs.cpu_sysinfo.cpu[CPU_IDLE], now);
745     cpu_stage(ksp[cpu]->ks_instance, COLLECTD_CPU_STATE_USER,
746               (derive_t)cs.cpu_sysinfo.cpu[CPU_USER], now);
747     cpu_stage(ksp[cpu]->ks_instance, COLLECTD_CPU_STATE_SYSTEM,
748               (derive_t)cs.cpu_sysinfo.cpu[CPU_KERNEL], now);
749     cpu_stage(ksp[cpu]->ks_instance, COLLECTD_CPU_STATE_WAIT,
750               (derive_t)cs.cpu_sysinfo.cpu[CPU_WAIT], now);
751   }
752   /* }}} #endif defined(HAVE_LIBKSTAT) */
753 
754 #elif CAN_USE_SYSCTL /* {{{ */
755   /* Only on (Open) BSD variant */
756   uint64_t cpuinfo[numcpu][CPUSTATES];
757   size_t cpuinfo_size;
758   int status;
759 
760   if (numcpu < 1) {
761     ERROR("cpu plugin: Could not determine number of "
762           "installed CPUs using sysctl(3).");
763     return -1;
764   }
765 
766   memset(cpuinfo, 0, sizeof(cpuinfo));
767 
768 #if defined(KERN_CP_TIME) && defined(KERNEL_NETBSD)
769   {
770     int mib[] = {CTL_KERN, KERN_CP_TIME};
771 
772     cpuinfo_size = sizeof(cpuinfo[0]) * numcpu * CPUSTATES;
773     status = sysctl(mib, 2, cpuinfo, &cpuinfo_size, NULL, 0);
774     if (status == -1) {
775       char errbuf[1024];
776 
777       ERROR("cpu plugin: sysctl failed: %s.",
778             sstrerror(errno, errbuf, sizeof(errbuf)));
779       return -1;
780     }
781     if (cpuinfo_size == (sizeof(cpuinfo[0]) * CPUSTATES)) {
782       numcpu = 1;
783     }
784   }
785 #else /* defined(KERN_CP_TIME) && defined(KERNEL_NETBSD) */
786 #if defined(KERN_CPTIME2)
787   if (numcpu > 1) {
788     for (int i = 0; i < numcpu; i++) {
789       int mib[] = {CTL_KERN, KERN_CPTIME2, i};
790 
791       cpuinfo_size = sizeof(cpuinfo[0]);
792 
793       status = sysctl(mib, STATIC_ARRAY_SIZE(mib), cpuinfo[i], &cpuinfo_size,
794                       NULL, 0);
795       if (status == -1) {
796         ERROR("cpu plugin: sysctl failed: %s.", STRERRNO);
797         return -1;
798       }
799     }
800   } else
801 #endif /* defined(KERN_CPTIME2) */
802   {
803     int mib[] = {CTL_KERN, KERN_CPTIME};
804     long cpuinfo_tmp[CPUSTATES];
805 
806     cpuinfo_size = sizeof(cpuinfo_tmp);
807 
808     status = sysctl(mib, STATIC_ARRAY_SIZE(mib), &cpuinfo_tmp, &cpuinfo_size,
809                     NULL, 0);
810     if (status == -1) {
811       ERROR("cpu plugin: sysctl failed: %s.", STRERRNO);
812       return -1;
813     }
814 
815     for (int i = 0; i < CPUSTATES; i++) {
816       cpuinfo[0][i] = cpuinfo_tmp[i];
817     }
818   }
819 #endif /* defined(KERN_CP_TIME) && defined(KERNEL_NETBSD) */
820 
821   for (int i = 0; i < numcpu; i++) {
822     cpu_stage(i, COLLECTD_CPU_STATE_USER, (derive_t)cpuinfo[i][CP_USER], now);
823     cpu_stage(i, COLLECTD_CPU_STATE_NICE, (derive_t)cpuinfo[i][CP_NICE], now);
824     cpu_stage(i, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cpuinfo[i][CP_SYS], now);
825     cpu_stage(i, COLLECTD_CPU_STATE_IDLE, (derive_t)cpuinfo[i][CP_IDLE], now);
826     cpu_stage(i, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[i][CP_INTR],
827               now);
828   }
829   /* }}} #endif CAN_USE_SYSCTL */
830 
831 #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES) /* {{{  \
832                                                                         */
833   /* Only on BSD variant */
834   long cpuinfo[maxcpu][CPUSTATES];
835   size_t cpuinfo_size;
836 
837   memset(cpuinfo, 0, sizeof(cpuinfo));
838 
839   cpuinfo_size = sizeof(cpuinfo);
840   if (sysctlbyname("kern.cp_times", &cpuinfo, &cpuinfo_size, NULL, 0) < 0) {
841     ERROR("cpu plugin: sysctlbyname failed: %s.", STRERRNO);
842     return -1;
843   }
844 
845   for (int i = 0; i < numcpu; i++) {
846     cpu_stage(i, COLLECTD_CPU_STATE_USER, (derive_t)cpuinfo[i][CP_USER], now);
847     cpu_stage(i, COLLECTD_CPU_STATE_NICE, (derive_t)cpuinfo[i][CP_NICE], now);
848     cpu_stage(i, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cpuinfo[i][CP_SYS], now);
849     cpu_stage(i, COLLECTD_CPU_STATE_IDLE, (derive_t)cpuinfo[i][CP_IDLE], now);
850     cpu_stage(i, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[i][CP_INTR],
851               now);
852   }
853   /* }}} #endif HAVE_SYSCTL_KERN_CP_TIMES */
854 
855 #elif defined(HAVE_SYSCTLBYNAME) /* {{{ */
856   /* Only on BSD variant */
857   long cpuinfo[CPUSTATES];
858   size_t cpuinfo_size;
859 
860   cpuinfo_size = sizeof(cpuinfo);
861 
862   if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0) {
863     ERROR("cpu plugin: sysctlbyname failed: %s.", STRERRNO);
864     return -1;
865   }
866 
867   cpu_stage(0, COLLECTD_CPU_STATE_USER, (derive_t)cpuinfo[CP_USER], now);
868   cpu_stage(0, COLLECTD_CPU_STATE_NICE, (derive_t)cpuinfo[CP_NICE], now);
869   cpu_stage(0, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cpuinfo[CP_SYS], now);
870   cpu_stage(0, COLLECTD_CPU_STATE_IDLE, (derive_t)cpuinfo[CP_IDLE], now);
871   cpu_stage(0, COLLECTD_CPU_STATE_INTERRUPT, (derive_t)cpuinfo[CP_INTR], now);
872   /* }}} #endif HAVE_SYSCTLBYNAME */
873 
874 #elif defined(HAVE_LIBSTATGRAB) /* {{{ */
875   sg_cpu_stats *cs;
876   cs = sg_get_cpu_stats();
877 
878   if (cs == NULL) {
879     ERROR("cpu plugin: sg_get_cpu_stats failed.");
880     return -1;
881   }
882 
883   cpu_state(0, COLLECTD_CPU_STATE_IDLE, (derive_t)cs->idle);
884   cpu_state(0, COLLECTD_CPU_STATE_NICE, (derive_t)cs->nice);
885   cpu_state(0, COLLECTD_CPU_STATE_SWAP, (derive_t)cs->swap);
886   cpu_state(0, COLLECTD_CPU_STATE_SYSTEM, (derive_t)cs->kernel);
887   cpu_state(0, COLLECTD_CPU_STATE_USER, (derive_t)cs->user);
888   cpu_state(0, COLLECTD_CPU_STATE_WAIT, (derive_t)cs->iowait);
889   /* }}} #endif HAVE_LIBSTATGRAB */
890 
891 #elif defined(HAVE_PERFSTAT) /* {{{ */
892   perfstat_id_t id;
893   int cpus;
894 
895   numcpu = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
896   if (numcpu == -1) {
897     WARNING("cpu plugin: perfstat_cpu: %s", STRERRNO);
898     return -1;
899   }
900 
901   if (pnumcpu != numcpu || perfcpu == NULL) {
902     free(perfcpu);
903     perfcpu = malloc(numcpu * sizeof(perfstat_cpu_t));
904   }
905   pnumcpu = numcpu;
906 
907   id.name[0] = '\0';
908   if ((cpus = perfstat_cpu(&id, perfcpu, sizeof(perfstat_cpu_t), numcpu)) < 0) {
909     WARNING("cpu plugin: perfstat_cpu: %s", STRERRNO);
910     return -1;
911   }
912 
913   for (int i = 0; i < cpus; i++) {
914     cpu_stage(i, COLLECTD_CPU_STATE_IDLE, (derive_t)perfcpu[i].idle, now);
915     cpu_stage(i, COLLECTD_CPU_STATE_SYSTEM, (derive_t)perfcpu[i].sys, now);
916     cpu_stage(i, COLLECTD_CPU_STATE_USER, (derive_t)perfcpu[i].user, now);
917     cpu_stage(i, COLLECTD_CPU_STATE_WAIT, (derive_t)perfcpu[i].wait, now);
918   }
919 #endif                       /* }}} HAVE_PERFSTAT */
920 
921   cpu_commit();
922   cpu_reset();
923   return 0;
924 }
925 
module_register(void)926 void module_register(void) {
927   plugin_register_init("cpu", init);
928   plugin_register_config("cpu", cpu_config, config_keys, config_keys_num);
929   plugin_register_read("cpu", cpu_read);
930 } /* void module_register */
931