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