1 /**
2 * collectd - src/processes.c
3 * Copyright (C) 2005 Lyonel Vincent
4 * Copyright (C) 2006-2017 Florian octo Forster
5 * Copyright (C) 2008 Oleg King
6 * Copyright (C) 2009 Sebastian Harl
7 * Copyright (C) 2009 Andrés J. Díaz
8 * Copyright (C) 2009 Manuel Sanmartin
9 * Copyright (C) 2010 Clément Stenac
10 * Copyright (C) 2012 Cosmin Ioiart
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 *
26 * Authors:
27 * Lyonel Vincent <lyonel at ezix.org>
28 * Florian octo Forster <octo at collectd.org>
29 * Oleg King <king2 at kaluga.ru>
30 * Sebastian Harl <sh at tokkee.org>
31 * Andrés J. Díaz <ajdiaz at connectical.com>
32 * Manuel Sanmartin
33 * Clément Stenac <clement.stenac at diwi.org>
34 * Cosmin Ioiart <cioiart at gmail.com>
35 * Pavel Rochnyack <pavel2000 at ngs.ru>
36 * Wilfried Goesgens <dothebart at citadel.org>
37 **/
38
39 #include "collectd.h"
40
41 #include "plugin.h"
42 #include "utils/common/common.h"
43
44 #if HAVE_LIBTASKSTATS
45 #include "utils/taskstats/taskstats.h"
46 #include "utils_complain.h"
47 #endif
48
49 /* Include header files for the mach system, if they exist.. */
50 #if HAVE_THREAD_INFO
51 #if HAVE_MACH_MACH_INIT_H
52 #include <mach/mach_init.h>
53 #endif
54 #if HAVE_MACH_HOST_PRIV_H
55 #include <mach/host_priv.h>
56 #endif
57 #if HAVE_MACH_MACH_ERROR_H
58 #include <mach/mach_error.h>
59 #endif
60 #if HAVE_MACH_MACH_HOST_H
61 #include <mach/mach_host.h>
62 #endif
63 #if HAVE_MACH_MACH_PORT_H
64 #include <mach/mach_port.h>
65 #endif
66 #if HAVE_MACH_MACH_TYPES_H
67 #include <mach/mach_types.h>
68 #endif
69 #if HAVE_MACH_MESSAGE_H
70 #include <mach/message.h>
71 #endif
72 #if HAVE_MACH_PROCESSOR_SET_H
73 #include <mach/processor_set.h>
74 #endif
75 #if HAVE_MACH_TASK_H
76 #include <mach/task.h>
77 #endif
78 #if HAVE_MACH_THREAD_ACT_H
79 #include <mach/thread_act.h>
80 #endif
81 #if HAVE_MACH_VM_REGION_H
82 #include <mach/vm_region.h>
83 #endif
84 #if HAVE_MACH_VM_MAP_H
85 #include <mach/vm_map.h>
86 #endif
87 #if HAVE_MACH_VM_PROT_H
88 #include <mach/vm_prot.h>
89 #endif
90 #if HAVE_SYS_SYSCTL_H
91 #include <sys/sysctl.h>
92 #endif
93 /* #endif HAVE_THREAD_INFO */
94
95 #elif KERNEL_LINUX
96 #if HAVE_LINUX_CONFIG_H
97 #include <linux/config.h>
98 #endif
99 #ifndef CONFIG_HZ
100 #define CONFIG_HZ 100
101 #endif
102 /* #endif KERNEL_LINUX */
103
104 #elif HAVE_LIBKVM_GETPROCS && \
105 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
106 HAVE_STRUCT_KINFO_PROC2_NETBSD)
107 #include <kvm.h>
108 #include <sys/param.h>
109 #ifndef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
110 #include <sys/proc.h>
111 #endif
112 #include <sys/sysctl.h>
113 #if defined(__FreeBSD__) || defined(__DragonFly__)
114 #include <sys/user.h>
115 #endif
116 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
117 * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
118
119 #elif HAVE_PROCINFO_H
120 #include <procinfo.h>
121 #include <sys/types.h>
122
123 #define MAXPROCENTRY 32
124 #define MAXTHRDENTRY 16
125 #define MAXARGLN 1024
126 /* #endif HAVE_PROCINFO_H */
127
128 #elif KERNEL_SOLARIS
129 /* Hack: Avoid #error when building a 32-bit binary with
130 * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one
131 * of the structures in <sys/procfs.h> uses an off_t, but that
132 * isn't relevant to our usage of procfs. */
133 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
134 #define SAVE_FOB_64
135 #undef _FILE_OFFSET_BITS
136 #endif
137
138 #include <procfs.h>
139
140 #ifdef SAVE_FOB_64
141 #define _FILE_OFFSET_BITS 64
142 #undef SAVE_FOB_64
143 #endif
144
145 #include <dirent.h>
146 #include <sys/user.h>
147
148 #ifndef MAXCOMLEN
149 #define MAXCOMLEN 16
150 #endif
151
152 /* #endif KERNEL_SOLARIS */
153
154 #else
155 #error "No applicable input method."
156 #endif
157
158 #if HAVE_REGEX_H
159 #include <regex.h>
160 #endif
161
162 #if HAVE_KSTAT_H
163 #include <kstat.h>
164 #endif
165
166 #ifdef HAVE_SYS_CAPABILITY_H
167 #include <sys/capability.h>
168 #endif
169
170 #ifndef CMDLINE_BUFFER_SIZE
171 #if defined(ARG_MAX) && (ARG_MAX < 4096)
172 #define CMDLINE_BUFFER_SIZE ARG_MAX
173 #else
174 #define CMDLINE_BUFFER_SIZE 4096
175 #endif
176 #endif
177
178 #define PROCSTAT_NAME_LEN 256
179 typedef struct process_entry_s {
180 unsigned long id;
181 char name[PROCSTAT_NAME_LEN];
182
183 unsigned long num_proc;
184 unsigned long num_lwp;
185 unsigned long num_fd;
186 unsigned long num_maps;
187 unsigned long vmem_size;
188 unsigned long vmem_rss;
189 unsigned long vmem_data;
190 unsigned long vmem_code;
191 unsigned long stack_size;
192
193 derive_t vmem_minflt_counter;
194 derive_t vmem_majflt_counter;
195
196 derive_t cpu_user_counter;
197 derive_t cpu_system_counter;
198
199 /* io data */
200 derive_t io_rchar;
201 derive_t io_wchar;
202 derive_t io_syscr;
203 derive_t io_syscw;
204 derive_t io_diskr;
205 derive_t io_diskw;
206 bool has_io;
207
208 derive_t cswitch_vol;
209 derive_t cswitch_invol;
210 bool has_cswitch;
211
212 #if HAVE_LIBTASKSTATS
213 ts_delay_t delay;
214 #endif
215 bool has_delay;
216
217 bool has_fd;
218
219 bool has_maps;
220 } process_entry_t;
221
222 typedef struct collectd_procstat_entry_s {
223 unsigned long id;
224 unsigned char age;
225
226 derive_t vmem_minflt_counter;
227 derive_t vmem_majflt_counter;
228
229 derive_t cpu_user_counter;
230 derive_t cpu_system_counter;
231
232 /* io data */
233 derive_t io_rchar;
234 derive_t io_wchar;
235 derive_t io_syscr;
236 derive_t io_syscw;
237 derive_t io_diskr;
238 derive_t io_diskw;
239
240 derive_t cswitch_vol;
241 derive_t cswitch_invol;
242
243 #if HAVE_LIBTASKSTATS
244 value_to_rate_state_t delay_cpu;
245 value_to_rate_state_t delay_blkio;
246 value_to_rate_state_t delay_swapin;
247 value_to_rate_state_t delay_freepages;
248 #endif
249
250 struct collectd_procstat_entry_s *next;
251 } collectd_procstat_entry_t;
252
253 typedef struct collectd_procstat {
254 char name[PROCSTAT_NAME_LEN];
255 #if HAVE_REGEX_H
256 regex_t *re;
257 #endif
258
259 unsigned long num_proc;
260 unsigned long num_lwp;
261 unsigned long num_fd;
262 unsigned long num_maps;
263 unsigned long vmem_size;
264 unsigned long vmem_rss;
265 unsigned long vmem_data;
266 unsigned long vmem_code;
267 unsigned long stack_size;
268
269 derive_t vmem_minflt_counter;
270 derive_t vmem_majflt_counter;
271
272 derive_t cpu_user_counter;
273 derive_t cpu_system_counter;
274
275 /* io data */
276 derive_t io_rchar;
277 derive_t io_wchar;
278 derive_t io_syscr;
279 derive_t io_syscw;
280 derive_t io_diskr;
281 derive_t io_diskw;
282
283 derive_t cswitch_vol;
284 derive_t cswitch_invol;
285
286 /* Linux Delay Accounting. Unit is ns/s. */
287 gauge_t delay_cpu;
288 gauge_t delay_blkio;
289 gauge_t delay_swapin;
290 gauge_t delay_freepages;
291
292 bool report_fd_num;
293 bool report_maps_num;
294 bool report_ctx_switch;
295 bool report_delay;
296
297 struct collectd_procstat *next;
298 struct collectd_procstat_entry_s *instances;
299 } collectd_procstat_t;
300
301 static collectd_procstat_t *list_head_g;
302
303 static bool want_init = true;
304 static bool report_ctx_switch;
305 static bool report_fd_num;
306 static bool report_maps_num;
307 static bool report_delay;
308
309 #if HAVE_THREAD_INFO
310 static mach_port_t port_host_self;
311 static mach_port_t port_task_self;
312
313 static processor_set_name_array_t pset_list;
314 static mach_msg_type_number_t pset_list_len;
315 /* #endif HAVE_THREAD_INFO */
316
317 #elif KERNEL_LINUX
318 static long pagesize_g;
319 static void ps_fill_details(const collectd_procstat_t *ps, process_entry_t *entry);
320 /* #endif KERNEL_LINUX */
321
322 #elif HAVE_LIBKVM_GETPROCS && \
323 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
324 HAVE_STRUCT_KINFO_PROC2_NETBSD)
325 static int pagesize;
326
327 #if KERNEL_NETBSD
328 int maxslp;
329 #endif
330
331 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
332 * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
333
334 #elif HAVE_PROCINFO_H
335 static struct procentry64 procentry[MAXPROCENTRY];
336 static struct thrdentry64 thrdentry[MAXTHRDENTRY];
337 static int pagesize;
338
339 #ifndef _AIXVERSION_610
340 int getprocs64(void *procsinfo, int sizproc, void *fdsinfo, int sizfd,
341 pid_t *index, int count);
342 int getthrds64(pid_t, void *, int, tid64_t *, int);
343 #endif
344 int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
345 #endif /* HAVE_PROCINFO_H */
346
347 #if HAVE_LIBTASKSTATS
348 static ts_t *taskstats_handle;
349 #endif
350
351 /* put name of process from config to list_head_g tree
352 * list_head_g is a list of 'procstat_t' structs with
353 * processes names we want to watch */
ps_list_register(const char * name,const char * regexp)354 static collectd_procstat_t *ps_list_register(const char *name, const char *regexp) {
355 collectd_procstat_t *new;
356 collectd_procstat_t *ptr;
357 int status;
358
359 new = calloc(1, sizeof(*new));
360 if (new == NULL) {
361 ERROR("processes plugin: ps_list_register: calloc failed.");
362 return NULL;
363 }
364 sstrncpy(new->name, name, sizeof(new->name));
365
366 new->io_rchar = -1;
367 new->io_wchar = -1;
368 new->io_syscr = -1;
369 new->io_syscw = -1;
370 new->io_diskr = -1;
371 new->io_diskw = -1;
372 new->cswitch_vol = -1;
373 new->cswitch_invol = -1;
374
375 new->report_fd_num = report_fd_num;
376 new->report_maps_num = report_maps_num;
377 new->report_ctx_switch = report_ctx_switch;
378 new->report_delay = report_delay;
379
380 #if HAVE_REGEX_H
381 if (regexp != NULL) {
382 DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
383 name);
384 new->re = malloc(sizeof(*new->re));
385 if (new->re == NULL) {
386 ERROR("processes plugin: ps_list_register: malloc failed.");
387 sfree(new);
388 return NULL;
389 }
390
391 status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
392 if (status != 0) {
393 DEBUG("ProcessMatch: compiling the regular expression \"%s\" failed.",
394 regexp);
395 sfree(new->re);
396 sfree(new);
397 return NULL;
398 }
399 }
400 #else
401 if (regexp != NULL) {
402 ERROR("processes plugin: ps_list_register: "
403 "Regular expression \"%s\" found in config "
404 "file, but support for regular expressions "
405 "has been disabled at compile time.",
406 regexp);
407 sfree(new);
408 return NULL;
409 }
410 #endif
411
412 for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) {
413 if (strcmp(ptr->name, name) == 0) {
414 WARNING("processes plugin: You have configured more "
415 "than one `Process' or "
416 "`ProcessMatch' with the same name. "
417 "All but the first setting will be "
418 "ignored.");
419 #if HAVE_REGEX_H
420 sfree(new->re);
421 #endif
422 sfree(new);
423 return NULL;
424 }
425
426 if (ptr->next == NULL)
427 break;
428 }
429
430 if (ptr == NULL)
431 list_head_g = new;
432 else
433 ptr->next = new;
434
435 return new;
436 } /* void ps_list_register */
437
438 /* try to match name against entry, returns 1 if success */
ps_list_match(const char * name,const char * cmdline,collectd_procstat_t * ps)439 static int ps_list_match(const char *name, const char *cmdline,
440 collectd_procstat_t *ps) {
441 #if HAVE_REGEX_H
442 if (ps->re != NULL) {
443 int status;
444 const char *str;
445
446 str = cmdline;
447 if ((str == NULL) || (str[0] == 0))
448 str = name;
449
450 assert(str != NULL);
451
452 status = regexec(ps->re, str,
453 /* nmatch = */ 0,
454 /* pmatch = */ NULL,
455 /* eflags = */ 0);
456 if (status == 0)
457 return 1;
458 } else
459 #endif
460 if (strcmp(ps->name, name) == 0)
461 return 1;
462
463 return 0;
464 } /* int ps_list_match */
465
ps_update_counter(derive_t * group_counter,derive_t * curr_counter,derive_t new_counter)466 static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
467 derive_t new_counter) {
468 unsigned long curr_value;
469
470 if (want_init) {
471 *curr_counter = new_counter;
472 return;
473 }
474
475 if (new_counter < *curr_counter)
476 curr_value = new_counter + (ULONG_MAX - *curr_counter);
477 else
478 curr_value = new_counter - *curr_counter;
479
480 if (*group_counter == -1)
481 *group_counter = 0;
482
483 *curr_counter = new_counter;
484 *group_counter += curr_value;
485 }
486
487 #if HAVE_LIBTASKSTATS
ps_update_delay_one(gauge_t * out_rate_sum,value_to_rate_state_t * state,uint64_t cnt,cdtime_t t)488 static void ps_update_delay_one(gauge_t *out_rate_sum,
489 value_to_rate_state_t *state, uint64_t cnt,
490 cdtime_t t) {
491 gauge_t rate = NAN;
492 int status = value_to_rate(&rate, (value_t){.counter = (counter_t)cnt},
493 DS_TYPE_COUNTER, t, state);
494 if ((status != 0) || isnan(rate)) {
495 return;
496 }
497
498 if (isnan(*out_rate_sum)) {
499 *out_rate_sum = rate;
500 } else {
501 *out_rate_sum += rate;
502 }
503 }
504
ps_update_delay(collectd_procstat_t * out,collectd_procstat_entry_t * prev,process_entry_t * curr)505 static void ps_update_delay(collectd_procstat_t *out, collectd_procstat_entry_t *prev,
506 process_entry_t *curr) {
507 cdtime_t now = cdtime();
508
509 ps_update_delay_one(&out->delay_cpu, &prev->delay_cpu, curr->delay.cpu_ns,
510 now);
511 ps_update_delay_one(&out->delay_blkio, &prev->delay_blkio,
512 curr->delay.blkio_ns, now);
513 ps_update_delay_one(&out->delay_swapin, &prev->delay_swapin,
514 curr->delay.swapin_ns, now);
515 ps_update_delay_one(&out->delay_freepages, &prev->delay_freepages,
516 curr->delay.freepages_ns, now);
517 }
518 #endif
519
520 /* add process entry to 'instances' of process 'name' (or refresh it) */
ps_list_add(const char * name,const char * cmdline,process_entry_t * entry)521 static void ps_list_add(const char *name, const char *cmdline,
522 process_entry_t *entry) {
523 collectd_procstat_entry_t *pse;
524
525 if (entry->id == 0)
526 return;
527
528 for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
529 if ((ps_list_match(name, cmdline, ps)) == 0)
530 continue;
531
532 #if KERNEL_LINUX
533 ps_fill_details(ps, entry);
534 #endif
535
536 for (pse = ps->instances; pse != NULL; pse = pse->next)
537 if ((pse->id == entry->id) || (pse->next == NULL))
538 break;
539
540 if ((pse == NULL) || (pse->id != entry->id)) {
541 collectd_procstat_entry_t *new;
542
543 new = calloc(1, sizeof(*new));
544 if (new == NULL)
545 return;
546 new->id = entry->id;
547
548 if (pse == NULL)
549 ps->instances = new;
550 else
551 pse->next = new;
552
553 pse = new;
554 }
555
556 pse->age = 0;
557
558 ps->num_proc += entry->num_proc;
559 ps->num_lwp += entry->num_lwp;
560 ps->num_fd += entry->num_fd;
561 ps->num_maps += entry->num_maps;
562 ps->vmem_size += entry->vmem_size;
563 ps->vmem_rss += entry->vmem_rss;
564 ps->vmem_data += entry->vmem_data;
565 ps->vmem_code += entry->vmem_code;
566 ps->stack_size += entry->stack_size;
567
568 if ((entry->io_rchar != -1) && (entry->io_wchar != -1)) {
569 ps_update_counter(&ps->io_rchar, &pse->io_rchar, entry->io_rchar);
570 ps_update_counter(&ps->io_wchar, &pse->io_wchar, entry->io_wchar);
571 }
572
573 if ((entry->io_syscr != -1) && (entry->io_syscw != -1)) {
574 ps_update_counter(&ps->io_syscr, &pse->io_syscr, entry->io_syscr);
575 ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
576 }
577
578 if ((entry->io_diskr != -1) && (entry->io_diskw != -1)) {
579 ps_update_counter(&ps->io_diskr, &pse->io_diskr, entry->io_diskr);
580 ps_update_counter(&ps->io_diskw, &pse->io_diskw, entry->io_diskw);
581 }
582
583 if ((entry->cswitch_vol != -1) && (entry->cswitch_invol != -1)) {
584 ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
585 entry->cswitch_vol);
586 ps_update_counter(&ps->cswitch_invol, &pse->cswitch_invol,
587 entry->cswitch_invol);
588 }
589
590 ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
591 entry->vmem_minflt_counter);
592 ps_update_counter(&ps->vmem_majflt_counter, &pse->vmem_majflt_counter,
593 entry->vmem_majflt_counter);
594
595 ps_update_counter(&ps->cpu_user_counter, &pse->cpu_user_counter,
596 entry->cpu_user_counter);
597 ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
598 entry->cpu_system_counter);
599
600 #if HAVE_LIBTASKSTATS
601 if (entry->has_delay)
602 ps_update_delay(ps, pse, entry);
603 #endif
604 }
605 }
606
607 /* remove old entries from instances of processes in list_head_g */
ps_list_reset(void)608 static void ps_list_reset(void) {
609 collectd_procstat_entry_t *pse;
610 collectd_procstat_entry_t *pse_prev;
611
612 for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
613 ps->num_proc = 0;
614 ps->num_lwp = 0;
615 ps->num_fd = 0;
616 ps->num_maps = 0;
617 ps->vmem_size = 0;
618 ps->vmem_rss = 0;
619 ps->vmem_data = 0;
620 ps->vmem_code = 0;
621 ps->stack_size = 0;
622
623 ps->delay_cpu = NAN;
624 ps->delay_blkio = NAN;
625 ps->delay_swapin = NAN;
626 ps->delay_freepages = NAN;
627
628 pse_prev = NULL;
629 pse = ps->instances;
630 while (pse != NULL) {
631 if (pse->age > 0) {
632 DEBUG("Removing this collectd_procstat entry cause it's too old: "
633 "id = %lu; name = %s;",
634 pse->id, ps->name);
635
636 if (pse_prev == NULL) {
637 ps->instances = pse->next;
638 free(pse);
639 pse = ps->instances;
640 } else {
641 pse_prev->next = pse->next;
642 free(pse);
643 pse = pse_prev->next;
644 }
645 } else {
646 pse->age = 1;
647 pse_prev = pse;
648 pse = pse->next;
649 }
650 } /* while (pse != NULL) */
651 } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
652 }
653
ps_tune_instance(oconfig_item_t * ci,collectd_procstat_t * ps)654 static void ps_tune_instance(oconfig_item_t *ci, collectd_procstat_t *ps) {
655 for (int i = 0; i < ci->children_num; i++) {
656 oconfig_item_t *c = ci->children + i;
657
658 if (strcasecmp(c->key, "CollectContextSwitch") == 0)
659 cf_util_get_boolean(c, &ps->report_ctx_switch);
660 else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
661 cf_util_get_boolean(c, &ps->report_fd_num);
662 else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
663 cf_util_get_boolean(c, &ps->report_maps_num);
664 else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
665 #if HAVE_LIBTASKSTATS
666 cf_util_get_boolean(c, &ps->report_delay);
667 #else
668 WARNING("processes plugin: The plugin has been compiled without support "
669 "for the \"CollectDelayAccounting\" option.");
670 #endif
671 } else {
672 ERROR("processes plugin: Option \"%s\" not allowed here.", c->key);
673 }
674 } /* for (ci->children) */
675 } /* void ps_tune_instance */
676
677 /* put all pre-defined 'Process' names from config to list_head_g tree */
ps_config(oconfig_item_t * ci)678 static int ps_config(oconfig_item_t *ci) {
679 #if KERNEL_LINUX
680 const size_t max_procname_len = 15;
681 #elif KERNEL_SOLARIS || KERNEL_FREEBSD || KERNEL_DRAGONFLY
682 const size_t max_procname_len = MAXCOMLEN - 1;
683 #endif
684
685 collectd_procstat_t *ps;
686
687 for (int i = 0; i < ci->children_num; ++i) {
688 oconfig_item_t *c = ci->children + i;
689
690 if (strcasecmp(c->key, "Process") == 0) {
691 if ((c->values_num != 1) || (OCONFIG_TYPE_STRING != c->values[0].type)) {
692 ERROR("processes plugin: `Process' expects exactly "
693 "one string argument (got %i).",
694 c->values_num);
695 continue;
696 }
697
698 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD || KERNEL_DRAGONFLY
699 if (strlen(c->values[0].value.string) > max_procname_len) {
700 WARNING("processes plugin: this platform has a %" PRIsz
701 " character limit "
702 "to process names. The `Process \"%s\"' option will "
703 "not work as expected.",
704 max_procname_len, c->values[0].value.string);
705 }
706 #endif
707
708 ps = ps_list_register(c->values[0].value.string, NULL);
709
710 if (c->children_num != 0 && ps != NULL)
711 ps_tune_instance(c, ps);
712 } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
713 if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
714 (OCONFIG_TYPE_STRING != c->values[1].type)) {
715 ERROR("processes plugin: `ProcessMatch' needs exactly "
716 "two string arguments (got %i).",
717 c->values_num);
718 continue;
719 }
720
721 ps = ps_list_register(c->values[0].value.string,
722 c->values[1].value.string);
723
724 if (c->children_num != 0 && ps != NULL)
725 ps_tune_instance(c, ps);
726 } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
727 cf_util_get_boolean(c, &report_ctx_switch);
728 } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
729 cf_util_get_boolean(c, &report_fd_num);
730 } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
731 cf_util_get_boolean(c, &report_maps_num);
732 } else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
733 #if HAVE_LIBTASKSTATS
734 cf_util_get_boolean(c, &report_delay);
735 #else
736 WARNING("processes plugin: The plugin has been compiled without support "
737 "for the \"CollectDelayAccounting\" option.");
738 #endif
739 } else {
740 ERROR("processes plugin: The `%s' configuration option is not "
741 "understood and will be ignored.",
742 c->key);
743 continue;
744 }
745 }
746
747 return 0;
748 }
749
ps_init(void)750 static int ps_init(void) {
751 #if HAVE_THREAD_INFO
752 kern_return_t status;
753
754 port_host_self = mach_host_self();
755 port_task_self = mach_task_self();
756
757 if (pset_list != NULL) {
758 vm_deallocate(port_task_self, (vm_address_t)pset_list,
759 pset_list_len * sizeof(processor_set_t));
760 pset_list = NULL;
761 pset_list_len = 0;
762 }
763
764 if ((status = host_processor_sets(port_host_self, &pset_list,
765 &pset_list_len)) != KERN_SUCCESS) {
766 ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
767 pset_list = NULL;
768 pset_list_len = 0;
769 return -1;
770 }
771 /* #endif HAVE_THREAD_INFO */
772
773 #elif KERNEL_LINUX
774 pagesize_g = sysconf(_SC_PAGESIZE);
775 DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
776
777 #if HAVE_LIBTASKSTATS
778 if (taskstats_handle == NULL) {
779 taskstats_handle = ts_create();
780 if (taskstats_handle == NULL) {
781 WARNING("processes plugin: Creating taskstats handle failed.");
782 }
783 }
784 #endif
785 /* #endif KERNEL_LINUX */
786
787 #elif HAVE_LIBKVM_GETPROCS && \
788 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
789 HAVE_STRUCT_KINFO_PROC2_NETBSD)
790 #if KERNEL_NETBSD
791 int mib[2];
792 size_t size;
793
794 mib[0] = CTL_VM;
795 mib[1] = VM_MAXSLP;
796 size = sizeof(maxslp);
797 if (sysctl(mib, 2, &maxslp, &size, NULL, 0) == -1)
798 maxslp = 20; /* reasonable default? */
799 #endif
800
801 pagesize = getpagesize();
802 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
803 * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
804
805 #elif HAVE_PROCINFO_H
806 pagesize = getpagesize();
807 #endif /* HAVE_PROCINFO_H */
808
809 return 0;
810 } /* int ps_init */
811
812 /* submit global state (e.g.: qty of zombies, running, etc..) */
ps_submit_state(const char * state,double value)813 static void ps_submit_state(const char *state, double value) {
814 value_list_t vl = VALUE_LIST_INIT;
815
816 vl.values = &(value_t){.gauge = value};
817 vl.values_len = 1;
818 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
819 sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
820 sstrncpy(vl.type, "ps_state", sizeof(vl.type));
821 sstrncpy(vl.type_instance, state, sizeof(vl.type_instance));
822
823 plugin_dispatch_values(&vl);
824 }
825
826 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
ps_submit_proc_list(collectd_procstat_t * ps)827 static void ps_submit_proc_list(collectd_procstat_t *ps) {
828 value_list_t vl = VALUE_LIST_INIT;
829 value_t values[2];
830
831 vl.values = values;
832 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
833 sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
834
835 sstrncpy(vl.type, "ps_vm", sizeof(vl.type));
836 vl.values[0].gauge = ps->vmem_size;
837 vl.values_len = 1;
838 plugin_dispatch_values(&vl);
839
840 sstrncpy(vl.type, "ps_rss", sizeof(vl.type));
841 vl.values[0].gauge = ps->vmem_rss;
842 vl.values_len = 1;
843 plugin_dispatch_values(&vl);
844
845 sstrncpy(vl.type, "ps_data", sizeof(vl.type));
846 vl.values[0].gauge = ps->vmem_data;
847 vl.values_len = 1;
848 plugin_dispatch_values(&vl);
849
850 sstrncpy(vl.type, "ps_code", sizeof(vl.type));
851 vl.values[0].gauge = ps->vmem_code;
852 vl.values_len = 1;
853 plugin_dispatch_values(&vl);
854
855 sstrncpy(vl.type, "ps_stacksize", sizeof(vl.type));
856 vl.values[0].gauge = ps->stack_size;
857 vl.values_len = 1;
858 plugin_dispatch_values(&vl);
859
860 sstrncpy(vl.type, "ps_cputime", sizeof(vl.type));
861 vl.values[0].derive = ps->cpu_user_counter;
862 vl.values[1].derive = ps->cpu_system_counter;
863 vl.values_len = 2;
864 plugin_dispatch_values(&vl);
865
866 sstrncpy(vl.type, "ps_count", sizeof(vl.type));
867 vl.values[0].gauge = ps->num_proc;
868 vl.values[1].gauge = ps->num_lwp;
869 vl.values_len = 2;
870 plugin_dispatch_values(&vl);
871
872 sstrncpy(vl.type, "ps_pagefaults", sizeof(vl.type));
873 vl.values[0].derive = ps->vmem_minflt_counter;
874 vl.values[1].derive = ps->vmem_majflt_counter;
875 vl.values_len = 2;
876 plugin_dispatch_values(&vl);
877
878 if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
879 sstrncpy(vl.type, "io_octets", sizeof(vl.type));
880 vl.values[0].derive = ps->io_rchar;
881 vl.values[1].derive = ps->io_wchar;
882 vl.values_len = 2;
883 plugin_dispatch_values(&vl);
884 }
885
886 if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
887 sstrncpy(vl.type, "io_ops", sizeof(vl.type));
888 vl.values[0].derive = ps->io_syscr;
889 vl.values[1].derive = ps->io_syscw;
890 vl.values_len = 2;
891 plugin_dispatch_values(&vl);
892 }
893
894 if ((ps->io_diskr != -1) && (ps->io_diskw != -1)) {
895 sstrncpy(vl.type, "disk_octets", sizeof(vl.type));
896 vl.values[0].derive = ps->io_diskr;
897 vl.values[1].derive = ps->io_diskw;
898 vl.values_len = 2;
899 plugin_dispatch_values(&vl);
900 }
901
902 if (ps->num_fd > 0) {
903 sstrncpy(vl.type, "file_handles", sizeof(vl.type));
904 vl.values[0].gauge = ps->num_fd;
905 vl.values_len = 1;
906 plugin_dispatch_values(&vl);
907 }
908
909 if (ps->num_maps > 0) {
910 sstrncpy(vl.type, "file_handles", sizeof(vl.type));
911 sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
912 vl.values[0].gauge = ps->num_maps;
913 vl.values_len = 1;
914 plugin_dispatch_values(&vl);
915 }
916
917 if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
918 sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
919 sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
920 vl.values[0].derive = ps->cswitch_vol;
921 vl.values_len = 1;
922 plugin_dispatch_values(&vl);
923
924 sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
925 sstrncpy(vl.type_instance, "involuntary", sizeof(vl.type_instance));
926 vl.values[0].derive = ps->cswitch_invol;
927 vl.values_len = 1;
928 plugin_dispatch_values(&vl);
929 }
930
931 /* The ps->delay_* metrics are in nanoseconds per second. Convert to seconds
932 * per second. */
933 gauge_t const delay_factor = 1000000000.0;
934
935 struct {
936 const char *type_instance;
937 gauge_t rate_ns;
938 } delay_metrics[] = {
939 {"delay-cpu", ps->delay_cpu},
940 {"delay-blkio", ps->delay_blkio},
941 {"delay-swapin", ps->delay_swapin},
942 {"delay-freepages", ps->delay_freepages},
943 };
944 for (size_t i = 0; i < STATIC_ARRAY_SIZE(delay_metrics); i++) {
945 if (isnan(delay_metrics[i].rate_ns)) {
946 continue;
947 }
948 sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
949 sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
950 sizeof(vl.type_instance));
951 vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
952 vl.values_len = 1;
953 plugin_dispatch_values(&vl);
954 }
955
956 DEBUG(
957 "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
958 "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
959 "vmem_code = %lu; "
960 "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
961 "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
962 "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
963 "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
964 "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
965 "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 "; "
966 "delay_cpu = %g; delay_blkio = %g; "
967 "delay_swapin = %g; delay_freepages = %g;",
968 ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
969 ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
970 ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
971 ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
972 ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
973 ps->cswitch_invol, ps->delay_cpu, ps->delay_blkio, ps->delay_swapin,
974 ps->delay_freepages);
975
976 } /* void ps_submit_proc_list */
977
978 #if KERNEL_LINUX || KERNEL_SOLARIS
ps_submit_fork_rate(derive_t value)979 static void ps_submit_fork_rate(derive_t value) {
980 value_list_t vl = VALUE_LIST_INIT;
981
982 vl.values = &(value_t){.derive = value};
983 vl.values_len = 1;
984 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
985 sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
986 sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
987 sstrncpy(vl.type_instance, "", sizeof(vl.type_instance));
988
989 plugin_dispatch_values(&vl);
990 }
991 #endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
992
993 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
994 #if KERNEL_LINUX
ps_read_tasks_status(process_entry_t * ps)995 static int ps_read_tasks_status(process_entry_t *ps) {
996 char dirname[64];
997 DIR *dh;
998 char filename[64];
999 FILE *fh;
1000 struct dirent *ent;
1001 derive_t cswitch_vol = 0;
1002 derive_t cswitch_invol = 0;
1003 char buffer[1024];
1004 char *fields[8];
1005 int numfields;
1006
1007 snprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
1008
1009 if ((dh = opendir(dirname)) == NULL) {
1010 DEBUG("Failed to open directory `%s'", dirname);
1011 return -1;
1012 }
1013
1014 while ((ent = readdir(dh)) != NULL) {
1015 char *tpid;
1016
1017 if (!isdigit((int)ent->d_name[0]))
1018 continue;
1019
1020 tpid = ent->d_name;
1021
1022 int r = snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status",
1023 ps->id, tpid);
1024 if ((size_t)r >= sizeof(filename)) {
1025 DEBUG("Filename too long: `%s'", filename);
1026 continue;
1027 }
1028
1029 if ((fh = fopen(filename, "r")) == NULL) {
1030 DEBUG("Failed to open file `%s'", filename);
1031 continue;
1032 }
1033
1034 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1035 derive_t tmp;
1036 char *endptr;
1037
1038 if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
1039 strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
1040 continue;
1041
1042 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1043
1044 if (numfields < 2)
1045 continue;
1046
1047 errno = 0;
1048 endptr = NULL;
1049 tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
1050 if ((errno == 0) && (endptr != fields[1])) {
1051 if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
1052 cswitch_vol += tmp;
1053 } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
1054 cswitch_invol += tmp;
1055 }
1056 }
1057 } /* while (fgets) */
1058
1059 if (fclose(fh)) {
1060 WARNING("processes: fclose: %s", STRERRNO);
1061 }
1062 }
1063 closedir(dh);
1064
1065 ps->cswitch_vol = cswitch_vol;
1066 ps->cswitch_invol = cswitch_invol;
1067
1068 return 0;
1069 } /* int *ps_read_tasks_status */
1070
1071 /* Read data from /proc/pid/status */
ps_read_status(long pid,process_entry_t * ps)1072 static int ps_read_status(long pid, process_entry_t *ps) {
1073 FILE *fh;
1074 char buffer[1024];
1075 char filename[64];
1076 unsigned long lib = 0;
1077 unsigned long exe = 0;
1078 unsigned long data = 0;
1079 unsigned long threads = 0;
1080 char *fields[8];
1081 int numfields;
1082
1083 snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1084 if ((fh = fopen(filename, "r")) == NULL)
1085 return -1;
1086
1087 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1088 unsigned long tmp;
1089 char *endptr;
1090
1091 if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
1092 continue;
1093
1094 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1095
1096 if (numfields < 2)
1097 continue;
1098
1099 errno = 0;
1100 endptr = NULL;
1101 tmp = strtoul(fields[1], &endptr, /* base = */ 10);
1102 if ((errno == 0) && (endptr != fields[1])) {
1103 if (strncmp(buffer, "VmData", 6) == 0) {
1104 data = tmp;
1105 } else if (strncmp(buffer, "VmLib", 5) == 0) {
1106 lib = tmp;
1107 } else if (strncmp(buffer, "VmExe", 5) == 0) {
1108 exe = tmp;
1109 } else if (strncmp(buffer, "Threads", 7) == 0) {
1110 threads = tmp;
1111 }
1112 }
1113 } /* while (fgets) */
1114
1115 if (fclose(fh)) {
1116 WARNING("processes: fclose: %s", STRERRNO);
1117 }
1118
1119 ps->vmem_data = data * 1024;
1120 ps->vmem_code = (exe + lib) * 1024;
1121 if (threads != 0)
1122 ps->num_lwp = threads;
1123
1124 return 0;
1125 } /* int *ps_read_status */
1126
ps_read_io(process_entry_t * ps)1127 static int ps_read_io(process_entry_t *ps) {
1128 FILE *fh;
1129 char buffer[1024];
1130 char filename[64];
1131
1132 char *fields[8];
1133 int numfields;
1134
1135 snprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
1136 if ((fh = fopen(filename, "r")) == NULL) {
1137 DEBUG("ps_read_io: Failed to open file `%s'", filename);
1138 return -1;
1139 }
1140
1141 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1142 derive_t *val = NULL;
1143 long long tmp;
1144 char *endptr;
1145
1146 if (strncasecmp(buffer, "rchar:", 6) == 0)
1147 val = &(ps->io_rchar);
1148 else if (strncasecmp(buffer, "wchar:", 6) == 0)
1149 val = &(ps->io_wchar);
1150 else if (strncasecmp(buffer, "syscr:", 6) == 0)
1151 val = &(ps->io_syscr);
1152 else if (strncasecmp(buffer, "syscw:", 6) == 0)
1153 val = &(ps->io_syscw);
1154 else if (strncasecmp(buffer, "read_bytes:", 11) == 0)
1155 val = &(ps->io_diskr);
1156 else if (strncasecmp(buffer, "write_bytes:", 12) == 0)
1157 val = &(ps->io_diskw);
1158 else
1159 continue;
1160
1161 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1162
1163 if (numfields < 2)
1164 continue;
1165
1166 errno = 0;
1167 endptr = NULL;
1168 tmp = strtoll(fields[1], &endptr, /* base = */ 10);
1169 if ((errno != 0) || (endptr == fields[1]))
1170 *val = -1;
1171 else
1172 *val = (derive_t)tmp;
1173 } /* while (fgets) */
1174
1175 if (fclose(fh)) {
1176 WARNING("processes: fclose: %s", STRERRNO);
1177 }
1178 return 0;
1179 } /* int ps_read_io (...) */
1180
ps_count_maps(pid_t pid)1181 static int ps_count_maps(pid_t pid) {
1182 FILE *fh;
1183 char buffer[1024];
1184 char filename[64];
1185 int count = 0;
1186
1187 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
1188 if ((fh = fopen(filename, "r")) == NULL) {
1189 DEBUG("ps_count_maps: Failed to open file `%s'", filename);
1190 return -1;
1191 }
1192
1193 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1194 if (strchr(buffer, '\n')) {
1195 count++;
1196 }
1197 } /* while (fgets) */
1198
1199 if (fclose(fh)) {
1200 WARNING("processes: fclose: %s", STRERRNO);
1201 }
1202 return count;
1203 } /* int ps_count_maps (...) */
1204
ps_count_fd(int pid)1205 static int ps_count_fd(int pid) {
1206 char dirname[64];
1207 DIR *dh;
1208 struct dirent *ent;
1209 int count = 0;
1210
1211 snprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
1212
1213 if ((dh = opendir(dirname)) == NULL) {
1214 DEBUG("Failed to open directory `%s'", dirname);
1215 return -1;
1216 }
1217 while ((ent = readdir(dh)) != NULL) {
1218 if (!isdigit((int)ent->d_name[0]))
1219 continue;
1220 else
1221 count++;
1222 }
1223 closedir(dh);
1224
1225 return (count >= 1) ? count : 1;
1226 } /* int ps_count_fd (pid) */
1227
1228 #if HAVE_LIBTASKSTATS
ps_delay(process_entry_t * ps)1229 static int ps_delay(process_entry_t *ps) {
1230 if (taskstats_handle == NULL) {
1231 return ENOTCONN;
1232 }
1233
1234 int status = ts_delay_by_tgid(taskstats_handle, (uint32_t)ps->id, &ps->delay);
1235 if (status == EPERM) {
1236 static c_complain_t c;
1237 #if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_ADMIN)
1238 if (check_capability(CAP_NET_ADMIN) != 0) {
1239 if (getuid() == 0) {
1240 c_complain(
1241 LOG_ERR, &c,
1242 "processes plugin: Reading Delay Accounting metric failed: %s. "
1243 "collectd is running as root, but missing the CAP_NET_ADMIN "
1244 "capability. The most common cause for this is that the init "
1245 "system is dropping capabilities.",
1246 STRERROR(status));
1247 } else {
1248 c_complain(
1249 LOG_ERR, &c,
1250 "processes plugin: Reading Delay Accounting metric failed: %s. "
1251 "collectd is not running as root and missing the CAP_NET_ADMIN "
1252 "capability. Either run collectd as root or grant it the "
1253 "CAP_NET_ADMIN capability using \"setcap cap_net_admin=ep " PREFIX
1254 "/sbin/collectd\".",
1255 STRERROR(status));
1256 }
1257 } else {
1258 ERROR("processes plugin: ts_delay_by_tgid failed: %s. The CAP_NET_ADMIN "
1259 "capability is available (I checked), so this error is utterly "
1260 "unexpected.",
1261 STRERROR(status));
1262 }
1263 #else
1264 c_complain(LOG_ERR, &c,
1265 "processes plugin: Reading Delay Accounting metric failed: %s. "
1266 "Reading Delay Accounting metrics requires root privileges.",
1267 STRERROR(status));
1268 #endif
1269 return status;
1270 } else if (status != 0) {
1271 ERROR("processes plugin: ts_delay_by_tgid failed: %s", STRERROR(status));
1272 return status;
1273 }
1274
1275 return 0;
1276 }
1277 #endif
1278
ps_fill_details(const collectd_procstat_t * ps,process_entry_t * entry)1279 static void ps_fill_details(const collectd_procstat_t *ps, process_entry_t *entry) {
1280 if (entry->has_io == false) {
1281 ps_read_io(entry);
1282 entry->has_io = true;
1283 }
1284
1285 if (ps->report_ctx_switch) {
1286 if (entry->has_cswitch == false) {
1287 ps_read_tasks_status(entry);
1288 entry->has_cswitch = true;
1289 }
1290 }
1291
1292 if (ps->report_maps_num) {
1293 int num_maps;
1294 if (entry->has_maps == false && (num_maps = ps_count_maps(entry->id)) > 0) {
1295 entry->num_maps = num_maps;
1296 }
1297 entry->has_maps = true;
1298 }
1299
1300 if (ps->report_fd_num) {
1301 int num_fd;
1302 if (entry->has_fd == false && (num_fd = ps_count_fd(entry->id)) > 0) {
1303 entry->num_fd = num_fd;
1304 }
1305 entry->has_fd = true;
1306 }
1307
1308 #if HAVE_LIBTASKSTATS
1309 if (ps->report_delay && !entry->has_delay) {
1310 if (ps_delay(entry) == 0) {
1311 entry->has_delay = true;
1312 }
1313 }
1314 #endif
1315 } /* void ps_fill_details (...) */
1316
1317 /* ps_read_process reads process counters on Linux. */
ps_read_process(long pid,process_entry_t * ps,char * state)1318 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1319 char filename[64];
1320 char buffer[1024];
1321
1322 char *fields[64];
1323 char fields_len;
1324
1325 size_t buffer_len;
1326
1327 char *buffer_ptr;
1328 size_t name_start_pos;
1329 size_t name_end_pos;
1330 size_t name_len;
1331
1332 derive_t cpu_user_counter;
1333 derive_t cpu_system_counter;
1334 long long unsigned vmem_size;
1335 long long unsigned vmem_rss;
1336 long long unsigned stack_size;
1337
1338 ssize_t status;
1339
1340 snprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
1341
1342 status = read_text_file_contents(filename, buffer, sizeof(buffer));
1343 if (status <= 0)
1344 return -1;
1345 buffer_len = (size_t)status;
1346
1347 /* The name of the process is enclosed in parens. Since the name can
1348 * contain parens itself, spaces, numbers and pretty much everything
1349 * else, use these to determine the process name. We don't use
1350 * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
1351 * otherwise be required to determine name_len. */
1352 name_start_pos = 0;
1353 while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
1354 name_start_pos++;
1355
1356 name_end_pos = buffer_len;
1357 while (name_end_pos > 0 && buffer[name_end_pos] != ')')
1358 name_end_pos--;
1359
1360 /* Either '(' or ')' is not found or they are in the wrong order.
1361 * Anyway, something weird that shouldn't happen ever. */
1362 if (name_start_pos >= name_end_pos) {
1363 ERROR("processes plugin: name_start_pos = %" PRIsz
1364 " >= name_end_pos = %" PRIsz,
1365 name_start_pos, name_end_pos);
1366 return -1;
1367 }
1368
1369 name_len = (name_end_pos - name_start_pos) - 1;
1370 if (name_len >= sizeof(ps->name))
1371 name_len = sizeof(ps->name) - 1;
1372
1373 sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
1374
1375 if ((buffer_len - name_end_pos) < 2)
1376 return -1;
1377 buffer_ptr = &buffer[name_end_pos + 2];
1378
1379 fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
1380 if (fields_len < 22) {
1381 DEBUG("processes plugin: ps_read_process (pid = %li):"
1382 " `%s' has only %i fields..",
1383 pid, filename, fields_len);
1384 return -1;
1385 }
1386
1387 *state = fields[0][0];
1388
1389 if (*state == 'Z') {
1390 ps->num_lwp = 0;
1391 ps->num_proc = 0;
1392 } else {
1393 ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
1394 if ((ps_read_status(pid, ps)) != 0) {
1395 /* No VMem data */
1396 ps->vmem_data = -1;
1397 ps->vmem_code = -1;
1398 DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
1399 }
1400 if (ps->num_lwp == 0)
1401 ps->num_lwp = 1;
1402 ps->num_proc = 1;
1403 }
1404
1405 /* Leave the rest at zero if this is only a zombi */
1406 if (ps->num_proc == 0) {
1407 DEBUG("processes plugin: This is only a zombie: pid = %li; "
1408 "name = %s;",
1409 pid, ps->name);
1410 return 0;
1411 }
1412
1413 cpu_user_counter = atoll(fields[11]);
1414 cpu_system_counter = atoll(fields[12]);
1415 vmem_size = atoll(fields[20]);
1416 vmem_rss = atoll(fields[21]);
1417 ps->vmem_minflt_counter = atol(fields[7]);
1418 ps->vmem_majflt_counter = atol(fields[9]);
1419
1420 {
1421 unsigned long long stack_start = atoll(fields[25]);
1422 unsigned long long stack_ptr = atoll(fields[26]);
1423
1424 stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
1425 : stack_ptr - stack_start;
1426 }
1427
1428 /* Convert jiffies to useconds */
1429 cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
1430 cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
1431 vmem_rss = vmem_rss * pagesize_g;
1432
1433 ps->cpu_user_counter = cpu_user_counter;
1434 ps->cpu_system_counter = cpu_system_counter;
1435 ps->vmem_size = (unsigned long)vmem_size;
1436 ps->vmem_rss = (unsigned long)vmem_rss;
1437 ps->stack_size = (unsigned long)stack_size;
1438
1439 /* no data by default. May be filled by ps_fill_details () */
1440 ps->io_rchar = -1;
1441 ps->io_wchar = -1;
1442 ps->io_syscr = -1;
1443 ps->io_syscw = -1;
1444 ps->io_diskr = -1;
1445 ps->io_diskw = -1;
1446
1447 ps->cswitch_vol = -1;
1448 ps->cswitch_invol = -1;
1449
1450 /* success */
1451 return 0;
1452 } /* int ps_read_process (...) */
1453
procs_running(void)1454 static int procs_running(void) {
1455 char buffer[65536] = {};
1456 char id[] = "procs_running "; /* white space terminated */
1457 char *running;
1458 char *endptr = NULL;
1459 long result = 0L;
1460
1461 ssize_t status;
1462
1463 status = read_file_contents("/proc/stat", buffer, sizeof(buffer) - 1);
1464 if (status <= 0) {
1465 return -1;
1466 }
1467
1468 /* the data contains :
1469 * the literal string 'procs_running',
1470 * a whitespace
1471 * the number of running processes.
1472 * The parser does include the white-space character.
1473 */
1474 running = strstr(buffer, id);
1475 if (!running) {
1476 WARNING("procs_running not found");
1477 return -1;
1478 }
1479 running += strlen(id);
1480
1481 result = strtol(running, &endptr, 10);
1482 if ((*running != '\0') && ((*endptr == '\0') || (*endptr == '\n'))) {
1483 return (int)result;
1484 }
1485
1486 return -1;
1487 }
1488
ps_get_cmdline(long pid,char * name,char * buf,size_t buf_len)1489 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
1490 char *buf_ptr;
1491 size_t len;
1492
1493 char file[PATH_MAX];
1494 int fd;
1495
1496 size_t n;
1497
1498 if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1499 return NULL;
1500
1501 snprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
1502
1503 errno = 0;
1504 fd = open(file, O_RDONLY);
1505 if (fd < 0) {
1506 /* ENOENT means the process exited while we were handling it.
1507 * Don't complain about this, it only fills the logs. */
1508 if (errno != ENOENT)
1509 WARNING("processes plugin: Failed to open `%s': %s.", file, STRERRNO);
1510 return NULL;
1511 }
1512
1513 buf_ptr = buf;
1514 len = buf_len;
1515
1516 n = 0;
1517
1518 while (42) {
1519 ssize_t status;
1520
1521 status = read(fd, (void *)buf_ptr, len);
1522
1523 if (status < 0) {
1524
1525 if ((EAGAIN == errno) || (EINTR == errno))
1526 continue;
1527
1528 WARNING("processes plugin: Failed to read from `%s': %s.", file,
1529 STRERRNO);
1530 close(fd);
1531 return NULL;
1532 }
1533
1534 n += status;
1535
1536 if (status == 0)
1537 break;
1538
1539 buf_ptr += status;
1540 len -= status;
1541
1542 if (len == 0)
1543 break;
1544 }
1545
1546 close(fd);
1547
1548 if (0 == n) {
1549 /* cmdline not available; e.g. kernel thread, zombie */
1550 if (NULL == name)
1551 return NULL;
1552
1553 snprintf(buf, buf_len, "[%s]", name);
1554 return buf;
1555 }
1556
1557 assert(n <= buf_len);
1558
1559 if (n == buf_len)
1560 --n;
1561 buf[n] = '\0';
1562
1563 --n;
1564 /* remove trailing whitespace */
1565 while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
1566 buf[n] = '\0';
1567 --n;
1568 }
1569
1570 /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1571 while (n > 0) {
1572 if ('\0' == buf[n])
1573 buf[n] = ' ';
1574 --n;
1575 }
1576 return buf;
1577 } /* char *ps_get_cmdline (...) */
1578
read_fork_rate(void)1579 static int read_fork_rate(void) {
1580 FILE *proc_stat;
1581 char buffer[1024];
1582 value_t value;
1583 bool value_valid = 0;
1584
1585 proc_stat = fopen("/proc/stat", "r");
1586 if (proc_stat == NULL) {
1587 ERROR("processes plugin: fopen (/proc/stat) failed: %s", STRERRNO);
1588 return -1;
1589 }
1590
1591 while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
1592 int status;
1593 char *fields[3];
1594 int fields_num;
1595
1596 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1597 if (fields_num != 2)
1598 continue;
1599
1600 if (strcmp("processes", fields[0]) != 0)
1601 continue;
1602
1603 status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
1604 if (status == 0)
1605 value_valid = 1;
1606
1607 break;
1608 }
1609 fclose(proc_stat);
1610
1611 if (!value_valid)
1612 return -1;
1613
1614 ps_submit_fork_rate(value.derive);
1615 return 0;
1616 }
1617 #endif /*KERNEL_LINUX */
1618
1619 #if KERNEL_SOLARIS
ps_get_cmdline(long pid,char * name,char * buffer,size_t buffer_size)1620 static char *ps_get_cmdline(long pid,
1621 char *name __attribute__((unused)), /* {{{ */
1622 char *buffer, size_t buffer_size) {
1623 char path[PATH_MAX];
1624 psinfo_t info;
1625 ssize_t status;
1626
1627 snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
1628
1629 status = read_file_contents(path, &info, sizeof(info));
1630 if ((status < 0) || (((size_t)status) != sizeof(info))) {
1631 ERROR("processes plugin: Unexpected return value "
1632 "while reading \"%s\": "
1633 "Returned %zd but expected %" PRIsz ".",
1634 path, status, buffer_size);
1635 return NULL;
1636 }
1637
1638 sstrncpy(buffer, info.pr_psargs, buffer_size);
1639
1640 return buffer;
1641 } /* }}} int ps_get_cmdline */
1642
1643 /*
1644 * Reads process information on the Solaris OS. The information comes mainly
1645 * from
1646 * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
1647 * The values for input and ouput chars are calculated "by hand"
1648 * Added a few "solaris" specific process states as well
1649 */
ps_read_process(long pid,process_entry_t * ps,char * state)1650 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1651 char filename[64];
1652 char f_psinfo[64], f_usage[64];
1653 char *buffer;
1654
1655 pstatus_t *myStatus;
1656 psinfo_t *myInfo;
1657 prusage_t *myUsage;
1658
1659 snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1660 snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
1661 snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
1662
1663 buffer = calloc(1, sizeof(pstatus_t));
1664 read_file_contents(filename, buffer, sizeof(pstatus_t));
1665 myStatus = (pstatus_t *)buffer;
1666
1667 buffer = calloc(1, sizeof(psinfo_t));
1668 read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
1669 myInfo = (psinfo_t *)buffer;
1670
1671 buffer = calloc(1, sizeof(prusage_t));
1672 read_file_contents(f_usage, buffer, sizeof(prusage_t));
1673 myUsage = (prusage_t *)buffer;
1674
1675 sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
1676 ps->num_lwp = myStatus->pr_nlwp;
1677 if (myInfo->pr_wstat != 0) {
1678 ps->num_proc = 0;
1679 ps->num_lwp = 0;
1680 *state = (char)'Z';
1681
1682 sfree(myStatus);
1683 sfree(myInfo);
1684 sfree(myUsage);
1685 return 0;
1686 } else {
1687 ps->num_proc = 1;
1688 ps->num_lwp = myInfo->pr_nlwp;
1689 }
1690
1691 /*
1692 * Convert system time and user time from nanoseconds to microseconds
1693 * for compatibility with the linux module
1694 */
1695 ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
1696 ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
1697
1698 /*
1699 * Convert rssize from KB to bytes to be consistent w/ the linux module
1700 */
1701 ps->vmem_rss = myInfo->pr_rssize * 1024;
1702 ps->vmem_size = myInfo->pr_size * 1024;
1703 ps->vmem_minflt_counter = myUsage->pr_minf;
1704 ps->vmem_majflt_counter = myUsage->pr_majf;
1705
1706 /*
1707 * TODO: Data and code segment calculations for Solaris
1708 */
1709
1710 ps->vmem_data = -1;
1711 ps->vmem_code = -1;
1712 ps->stack_size = myStatus->pr_stksize;
1713
1714 /*
1715 * TODO: File descriptor count for Solaris
1716 */
1717 ps->num_fd = 0;
1718
1719 /* Number of memory mappings */
1720 ps->num_maps = 0;
1721
1722 /*
1723 * Calculating input/ouput chars
1724 * Formula used is total chars / total blocks => chars/block
1725 * then convert input/output blocks to chars
1726 */
1727 ulong_t tot_chars = myUsage->pr_ioch;
1728 ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
1729 ulong_t chars_per_block = 1;
1730 if (tot_blocks != 0)
1731 chars_per_block = tot_chars / tot_blocks;
1732 ps->io_rchar = myUsage->pr_inblk * chars_per_block;
1733 ps->io_wchar = myUsage->pr_oublk * chars_per_block;
1734 ps->io_syscr = myUsage->pr_sysc;
1735 ps->io_syscw = myUsage->pr_sysc;
1736 ps->io_diskr = -1;
1737 ps->io_diskw = -1;
1738
1739 /*
1740 * TODO: context switch counters for Solaris
1741 */
1742 ps->cswitch_vol = -1;
1743 ps->cswitch_invol = -1;
1744
1745 /*
1746 * TODO: Find way of setting BLOCKED and PAGING status
1747 */
1748
1749 *state = (char)'R';
1750 if (myStatus->pr_flags & PR_ASLEEP)
1751 *state = (char)'S';
1752 else if (myStatus->pr_flags & PR_STOPPED)
1753 *state = (char)'T';
1754 else if (myStatus->pr_flags & PR_DETACH)
1755 *state = (char)'E';
1756 else if (myStatus->pr_flags & PR_DAEMON)
1757 *state = (char)'A';
1758 else if (myStatus->pr_flags & PR_ISSYS)
1759 *state = (char)'Y';
1760 else if (myStatus->pr_flags & PR_ORPHAN)
1761 *state = (char)'O';
1762
1763 sfree(myStatus);
1764 sfree(myInfo);
1765 sfree(myUsage);
1766
1767 return 0;
1768 }
1769
1770 /*
1771 * Reads the number of threads created since the last reboot. On Solaris these
1772 * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
1773 * The result is the sum for all the threads created on each cpu
1774 */
read_fork_rate(void)1775 static int read_fork_rate(void) {
1776 extern kstat_ctl_t *kc;
1777 derive_t result = 0;
1778
1779 if (kc == NULL)
1780 return -1;
1781
1782 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
1783 ksp_chain = ksp_chain->ks_next) {
1784 if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
1785 (strcmp(ksp_chain->ks_name, "sys") == 0) &&
1786 (strcmp(ksp_chain->ks_class, "misc") == 0)) {
1787 long long tmp;
1788
1789 kstat_read(kc, ksp_chain, NULL);
1790
1791 tmp = get_kstat_value(ksp_chain, "nthreads");
1792 if (tmp != -1LL)
1793 result += tmp;
1794 }
1795 }
1796
1797 ps_submit_fork_rate(result);
1798 return 0;
1799 }
1800 #endif /* KERNEL_SOLARIS */
1801
1802 #if HAVE_THREAD_INFO
mach_get_task_name(task_t t,int * pid,char * name,size_t name_max_len)1803 static int mach_get_task_name(task_t t, int *pid, char *name,
1804 size_t name_max_len) {
1805 int mib[4];
1806
1807 struct kinfo_proc kp;
1808 size_t kp_size;
1809
1810 mib[0] = CTL_KERN;
1811 mib[1] = KERN_PROC;
1812 mib[2] = KERN_PROC_PID;
1813
1814 if (pid_for_task(t, pid) != KERN_SUCCESS)
1815 return -1;
1816 mib[3] = *pid;
1817
1818 kp_size = sizeof(kp);
1819 if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
1820 return -1;
1821
1822 if (name_max_len > (MAXCOMLEN + 1))
1823 name_max_len = MAXCOMLEN + 1;
1824
1825 strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
1826 name[name_max_len - 1] = '\0';
1827
1828 DEBUG("pid = %i; name = %s;", *pid, name);
1829
1830 /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1831 * `top' does it, because it is a lot of work and only used when
1832 * debugging. -octo */
1833
1834 return 0;
1835 }
1836 #endif /* HAVE_THREAD_INFO */
1837 /* end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO */
1838
1839 /* do actual readings from kernel */
ps_read(void)1840 static int ps_read(void) {
1841 #if HAVE_THREAD_INFO
1842 kern_return_t status;
1843
1844 processor_set_t port_pset_priv;
1845
1846 task_array_t task_list;
1847 mach_msg_type_number_t task_list_len;
1848
1849 int task_pid;
1850 char task_name[MAXCOMLEN + 1];
1851
1852 thread_act_array_t thread_list;
1853 mach_msg_type_number_t thread_list_len;
1854 thread_basic_info_data_t thread_data;
1855 mach_msg_type_number_t thread_data_len;
1856
1857 int running = 0;
1858 int sleeping = 0;
1859 int zombies = 0;
1860 int stopped = 0;
1861 int blocked = 0;
1862
1863 collectd_procstat_t *ps;
1864 process_entry_t pse;
1865
1866 ps_list_reset();
1867
1868 /*
1869 * The Mach-concept is a little different from the traditional UNIX
1870 * concept: All the work is done in threads. Threads are contained in
1871 * `tasks'. Therefore, `task status' doesn't make much sense, since
1872 * it's actually a `thread status'.
1873 * Tasks are assigned to sets of processors, so that's where you go to
1874 * get a list.
1875 */
1876 for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
1877 if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
1878 &port_pset_priv)) != KERN_SUCCESS) {
1879 ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
1880 continue;
1881 }
1882
1883 if ((status = processor_set_tasks(port_pset_priv, &task_list,
1884 &task_list_len)) != KERN_SUCCESS) {
1885 ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
1886 mach_port_deallocate(port_task_self, port_pset_priv);
1887 continue;
1888 }
1889
1890 for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
1891 ps = NULL;
1892 if (mach_get_task_name(task_list[task], &task_pid, task_name,
1893 PROCSTAT_NAME_LEN) == 0) {
1894 /* search for at least one match */
1895 for (ps = list_head_g; ps != NULL; ps = ps->next)
1896 /* FIXME: cmdline should be here instead of NULL */
1897 if (ps_list_match(task_name, NULL, ps) == 1)
1898 break;
1899 }
1900
1901 /* Collect more detailed statistics for this process */
1902 if (ps != NULL) {
1903 task_basic_info_data_t task_basic_info;
1904 mach_msg_type_number_t task_basic_info_len;
1905 task_events_info_data_t task_events_info;
1906 mach_msg_type_number_t task_events_info_len;
1907 task_absolutetime_info_data_t task_absolutetime_info;
1908 mach_msg_type_number_t task_absolutetime_info_len;
1909
1910 memset(&pse, '\0', sizeof(pse));
1911 pse.id = task_pid;
1912
1913 task_basic_info_len = TASK_BASIC_INFO_COUNT;
1914 status = task_info(task_list[task], TASK_BASIC_INFO,
1915 (task_info_t)&task_basic_info, &task_basic_info_len);
1916 if (status != KERN_SUCCESS) {
1917 ERROR("task_info failed: %s", mach_error_string(status));
1918 continue; /* with next thread_list */
1919 }
1920
1921 task_events_info_len = TASK_EVENTS_INFO_COUNT;
1922 status =
1923 task_info(task_list[task], TASK_EVENTS_INFO,
1924 (task_info_t)&task_events_info, &task_events_info_len);
1925 if (status != KERN_SUCCESS) {
1926 ERROR("task_info failed: %s", mach_error_string(status));
1927 continue; /* with next thread_list */
1928 }
1929
1930 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1931 status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
1932 (task_info_t)&task_absolutetime_info,
1933 &task_absolutetime_info_len);
1934 if (status != KERN_SUCCESS) {
1935 ERROR("task_info failed: %s", mach_error_string(status));
1936 continue; /* with next thread_list */
1937 }
1938
1939 pse.num_proc++;
1940 pse.vmem_size = task_basic_info.virtual_size;
1941 pse.vmem_rss = task_basic_info.resident_size;
1942 /* Does not seem to be easily exposed */
1943 pse.vmem_data = 0;
1944 pse.vmem_code = 0;
1945
1946 pse.io_rchar = -1;
1947 pse.io_wchar = -1;
1948 pse.io_syscr = -1;
1949 pse.io_syscw = -1;
1950 pse.io_diskr = -1;
1951 pse.io_diskw = -1;
1952
1953 /* File descriptor count not implemented */
1954 pse.num_fd = 0;
1955
1956 /* Number of memory mappings */
1957 pse.num_maps = 0;
1958
1959 pse.vmem_minflt_counter = task_events_info.cow_faults;
1960 pse.vmem_majflt_counter = task_events_info.faults;
1961
1962 pse.cpu_user_counter = task_absolutetime_info.total_user;
1963 pse.cpu_system_counter = task_absolutetime_info.total_system;
1964
1965 /* context switch counters not implemented */
1966 pse.cswitch_vol = -1;
1967 pse.cswitch_invol = -1;
1968 }
1969
1970 status = task_threads(task_list[task], &thread_list, &thread_list_len);
1971 if (status != KERN_SUCCESS) {
1972 /* Apple's `top' treats this case a zombie. It
1973 * makes sense to some extend: A `zombie'
1974 * thread is nonsense, since the task/process
1975 * is dead. */
1976 zombies++;
1977 DEBUG("task_threads failed: %s", mach_error_string(status));
1978 if (task_list[task] != port_task_self)
1979 mach_port_deallocate(port_task_self, task_list[task]);
1980 continue; /* with next task_list */
1981 }
1982
1983 for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
1984 thread++) {
1985 thread_data_len = THREAD_BASIC_INFO_COUNT;
1986 status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
1987 (thread_info_t)&thread_data, &thread_data_len);
1988 if (status != KERN_SUCCESS) {
1989 ERROR("thread_info failed: %s", mach_error_string(status));
1990 if (task_list[task] != port_task_self)
1991 mach_port_deallocate(port_task_self, thread_list[thread]);
1992 continue; /* with next thread_list */
1993 }
1994
1995 if (ps != NULL)
1996 pse.num_lwp++;
1997
1998 switch (thread_data.run_state) {
1999 case TH_STATE_RUNNING:
2000 running++;
2001 break;
2002 case TH_STATE_STOPPED:
2003 /* What exactly is `halted'? */
2004 case TH_STATE_HALTED:
2005 stopped++;
2006 break;
2007 case TH_STATE_WAITING:
2008 sleeping++;
2009 break;
2010 case TH_STATE_UNINTERRUPTIBLE:
2011 blocked++;
2012 break;
2013 /* There is no `zombie' case here,
2014 * since there are no zombie-threads.
2015 * There's only zombie tasks, which are
2016 * handled above. */
2017 default:
2018 WARNING("Unknown thread status: %i", thread_data.run_state);
2019 break;
2020 } /* switch (thread_data.run_state) */
2021
2022 if (task_list[task] != port_task_self) {
2023 status = mach_port_deallocate(port_task_self, thread_list[thread]);
2024 if (status != KERN_SUCCESS)
2025 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2026 }
2027 } /* for (thread_list) */
2028
2029 if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
2030 thread_list_len * sizeof(thread_act_t))) !=
2031 KERN_SUCCESS) {
2032 ERROR("vm_deallocate failed: %s", mach_error_string(status));
2033 }
2034 thread_list = NULL;
2035 thread_list_len = 0;
2036
2037 /* Only deallocate the task port, if it isn't our own.
2038 * Don't know what would happen in that case, but this
2039 * is what Apple's top does.. ;) */
2040 if (task_list[task] != port_task_self) {
2041 status = mach_port_deallocate(port_task_self, task_list[task]);
2042 if (status != KERN_SUCCESS)
2043 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2044 }
2045
2046 if (ps != NULL)
2047 /* FIXME: cmdline should be here instead of NULL */
2048 ps_list_add(task_name, NULL, &pse);
2049 } /* for (task_list) */
2050
2051 if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
2052 task_list_len * sizeof(task_t))) !=
2053 KERN_SUCCESS) {
2054 ERROR("vm_deallocate failed: %s", mach_error_string(status));
2055 }
2056 task_list = NULL;
2057 task_list_len = 0;
2058
2059 if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
2060 KERN_SUCCESS) {
2061 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2062 }
2063 } /* for (pset_list) */
2064
2065 ps_submit_state("running", running);
2066 ps_submit_state("sleeping", sleeping);
2067 ps_submit_state("zombies", zombies);
2068 ps_submit_state("stopped", stopped);
2069 ps_submit_state("blocked", blocked);
2070
2071 for (ps = list_head_g; ps != NULL; ps = ps->next)
2072 ps_submit_proc_list(ps);
2073 /* #endif HAVE_THREAD_INFO */
2074
2075 #elif KERNEL_LINUX
2076 int running = 0;
2077 int sleeping = 0;
2078 int zombies = 0;
2079 int stopped = 0;
2080 int paging = 0;
2081 int blocked = 0;
2082
2083 struct dirent *ent;
2084 DIR *proc;
2085 long pid;
2086
2087 char cmdline[CMDLINE_BUFFER_SIZE];
2088
2089 int status;
2090 process_entry_t pse;
2091 char state;
2092
2093 running = sleeping = zombies = stopped = paging = blocked = 0;
2094 ps_list_reset();
2095
2096 if ((proc = opendir("/proc")) == NULL) {
2097 ERROR("Cannot open `/proc': %s", STRERRNO);
2098 return -1;
2099 }
2100
2101 while ((ent = readdir(proc)) != NULL) {
2102 if (!isdigit(ent->d_name[0]))
2103 continue;
2104
2105 if ((pid = atol(ent->d_name)) < 1)
2106 continue;
2107
2108 memset(&pse, 0, sizeof(pse));
2109 pse.id = pid;
2110
2111 status = ps_read_process(pid, &pse, &state);
2112 if (status != 0) {
2113 DEBUG("ps_read_process failed: %i", status);
2114 continue;
2115 }
2116
2117 switch (state) {
2118 case 'R':
2119 running++;
2120 break;
2121 case 'S':
2122 sleeping++;
2123 break;
2124 case 'D':
2125 blocked++;
2126 break;
2127 case 'Z':
2128 zombies++;
2129 break;
2130 case 'T':
2131 stopped++;
2132 break;
2133 case 'W':
2134 paging++;
2135 break;
2136 }
2137
2138 ps_list_add(pse.name,
2139 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
2140 }
2141
2142 closedir(proc);
2143
2144 /* get procs_running from /proc/stat
2145 * scanning /proc/stat AND computing other process stats takes too much time.
2146 * Consequently, the number of running processes based on the occurences
2147 * of 'R' as character indicating the running state is typically zero. Due
2148 * to processes are actually changing state during the evaluation of it's
2149 * stat(s).
2150 * The 'procs_running' number in /proc/stat on the other hand is more
2151 * accurate, and can be retrieved in a single 'read' call. */
2152 running = procs_running();
2153
2154 ps_submit_state("running", running);
2155 ps_submit_state("sleeping", sleeping);
2156 ps_submit_state("zombies", zombies);
2157 ps_submit_state("stopped", stopped);
2158 ps_submit_state("paging", paging);
2159 ps_submit_state("blocked", blocked);
2160
2161 for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2162 ps_submit_proc_list(ps_ptr);
2163
2164 read_fork_rate();
2165 /* #endif KERNEL_LINUX */
2166
2167 #elif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY)
2168 int running = 0;
2169 int sleeping = 0;
2170 int zombies = 0;
2171 int stopped = 0;
2172 int blocked = 0;
2173 int idle = 0;
2174 int wait = 0;
2175
2176 kvm_t *kd;
2177 char errbuf[_POSIX2_LINE_MAX];
2178 struct kinfo_proc *procs; /* array of processes */
2179 struct kinfo_proc *proc_ptr = NULL;
2180 int count; /* returns number of processes */
2181
2182 process_entry_t pse;
2183
2184 ps_list_reset();
2185
2186 /* Open the kvm interface, get a descriptor */
2187 kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
2188 if (kd == NULL) {
2189 ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2190 return 0;
2191 }
2192
2193 /* Get the list of processes. */
2194 procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
2195 if (procs == NULL) {
2196 ERROR("processes plugin: Cannot get kvm processes list: %s",
2197 kvm_geterr(kd));
2198 kvm_close(kd);
2199 return 0;
2200 }
2201
2202 /* Iterate through the processes in kinfo_proc */
2203 for (int i = 0; i < count; i++) {
2204 /* Create only one process list entry per _process_, i.e.
2205 * filter out threads (duplicate PID entries). */
2206 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2207 if ((proc_ptr == NULL) || (proc_ptr->kp_pid != procs[i].kp_pid)) {
2208 #else
2209 if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
2210 #endif
2211 char cmdline[CMDLINE_BUFFER_SIZE] = "";
2212 bool have_cmdline = 0;
2213
2214 proc_ptr = &(procs[i]);
2215 /* Don't probe system processes and processes without arguments */
2216 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2217 if ((procs[i].kp_flags & P_SYSTEM) == 0) {
2218 #else
2219 if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
2220 #endif
2221 char **argv;
2222 int argc;
2223 int status;
2224
2225 /* retrieve the arguments */
2226 argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
2227 argc = 0;
2228 if ((argv != NULL) && (argv[0] != NULL)) {
2229 while (argv[argc] != NULL)
2230 argc++;
2231
2232 status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2233 if (status < 0)
2234 WARNING("processes plugin: Command line did not fit into buffer.");
2235 else
2236 have_cmdline = 1;
2237 }
2238 } /* if (process has argument list) */
2239
2240 memset(&pse, 0, sizeof(pse));
2241 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2242 pse.id = procs[i].kp_pid;
2243 pse.num_lwp = procs[i].kp_nthreads;
2244
2245 pse.vmem_size = procs[i].kp_vm_map_size;
2246 pse.vmem_rss = procs[i].kp_vm_rssize * pagesize;
2247 pse.vmem_data = procs[i].kp_vm_dsize * pagesize;
2248 pse.vmem_code = procs[i].kp_vm_tsize * pagesize;
2249 pse.stack_size = procs[i].kp_vm_ssize * pagesize;
2250 pse.vmem_minflt_counter = procs[i].kp_ru.ru_minflt;
2251 pse.vmem_majflt_counter = procs[i].kp_ru.ru_majflt;
2252 #else
2253 pse.id = procs[i].ki_pid;
2254
2255 pse.num_lwp = procs[i].ki_numthreads;
2256
2257 pse.vmem_size = procs[i].ki_size;
2258 pse.vmem_rss = procs[i].ki_rssize * pagesize;
2259 pse.vmem_data = procs[i].ki_dsize * pagesize;
2260 pse.vmem_code = procs[i].ki_tsize * pagesize;
2261 pse.stack_size = procs[i].ki_ssize * pagesize;
2262 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
2263 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
2264 #endif
2265 pse.num_proc = 1;
2266
2267 pse.cpu_user_counter = 0;
2268 pse.cpu_system_counter = 0;
2269
2270 #ifdef HAVE_STRUCT_KINFO_PROC_FREEBSD
2271 /*
2272 * The u-area might be swapped out, and we can't get
2273 * at it because we have a crashdump and no swap.
2274 * If it's here fill in these fields, otherwise, just
2275 * leave them 0.
2276 */
2277 if (procs[i].ki_flag & P_INMEM) {
2278 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
2279 (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
2280 pse.cpu_system_counter =
2281 procs[i].ki_rusage.ru_stime.tv_usec +
2282 (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
2283 }
2284 #endif
2285
2286 /* no I/O data */
2287 pse.io_rchar = -1;
2288 pse.io_wchar = -1;
2289 pse.io_syscr = -1;
2290 pse.io_syscw = -1;
2291 pse.io_diskr = -1;
2292 pse.io_diskw = -1;
2293
2294 /* file descriptor count not implemented */
2295 pse.num_fd = 0;
2296
2297 /* Number of memory mappings */
2298 pse.num_maps = 0;
2299
2300 /* context switch counters not implemented */
2301 pse.cswitch_vol = -1;
2302 pse.cswitch_invol = -1;
2303
2304 #ifdef HAVE_STRUCT_KINFO_PROC_FREEBSD
2305 ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
2306
2307 switch (procs[i].ki_stat) {
2308 case SSLEEP:
2309 sleeping++;
2310 break;
2311 case SRUN:
2312 running++;
2313 break;
2314 case SWAIT:
2315 wait++;
2316 break;
2317 case SLOCK:
2318 blocked++;
2319 break;
2320 #else
2321 ps_list_add(procs[i].kp_comm, have_cmdline ? cmdline : NULL, &pse);
2322
2323 switch (procs[i].kp_stat) {
2324 case SACTIVE:
2325 switch(procs[i].kp_lwp.kl_stat) {
2326 case LSSLEEP:
2327 sleeping++;
2328 break;
2329 case LSRUN:
2330 running++;
2331 break;
2332 case LSSTOP:
2333 stopped++;
2334 break;
2335 }
2336 break;
2337 #endif
2338 case SSTOP:
2339 stopped++;
2340 break;
2341 case SZOMB:
2342 zombies++;
2343 break;
2344 case SIDL:
2345 idle++;
2346 break;
2347 }
2348 } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
2349 }
2350
2351 kvm_close(kd);
2352
2353 ps_submit_state("running", running);
2354 ps_submit_state("sleeping", sleeping);
2355 ps_submit_state("zombies", zombies);
2356 ps_submit_state("stopped", stopped);
2357 ps_submit_state("blocked", blocked);
2358 ps_submit_state("idle", idle);
2359 ps_submit_state("wait", wait);
2360
2361 for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2362 ps_submit_proc_list(ps_ptr);
2363 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
2364
2365 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC2_NETBSD
2366 int running = 0;
2367 int sleeping = 0;
2368 int zombies = 0;
2369 int stopped = 0;
2370 int blocked = 0;
2371 int idle = 0;
2372 int wait = 0;
2373
2374 kvm_t *kd;
2375 char errbuf[_POSIX2_LINE_MAX];
2376 struct kinfo_proc2 *procs; /* array of processes */
2377 struct kinfo_proc2 *proc_ptr = NULL;
2378 struct kinfo_proc2 *p;
2379 int count; /* returns number of processes */
2380 int i;
2381 int l, nlwps;
2382 struct kinfo_lwp *kl;
2383
2384 collectd_procstat_t *ps_ptr;
2385 process_entry_t pse;
2386
2387 ps_list_reset();
2388
2389 /* Open the kvm interface, get a descriptor */
2390 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
2391 if (kd == NULL) {
2392 ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2393 return (0);
2394 }
2395
2396 /* Get the list of processes. */
2397 procs =
2398 kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count);
2399 if (procs == NULL) {
2400 ERROR("processes plugin: Cannot get kvm processes list: %s",
2401 kvm_geterr(kd));
2402 kvm_close(kd);
2403 return (0);
2404 }
2405
2406 /* Iterate through the processes in kinfo_proc */
2407 for (i = 0; i < count; i++) {
2408 /* Create only one process list entry per _process_, i.e.
2409 * filter out threads (duplicate PID entries). */
2410 if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
2411 char cmdline[CMDLINE_BUFFER_SIZE] = "";
2412 _Bool have_cmdline = 0;
2413
2414 proc_ptr = &(procs[i]);
2415 /* Don't probe system processes and processes without arguments */
2416 if (((procs[i].p_flag & P_SYSTEM) == 0) && (procs[i].p_comm[0] != 0)) {
2417 char **argv;
2418 int argc;
2419 int status;
2420
2421 /* retrieve the arguments */
2422 argv = kvm_getargv2(kd, proc_ptr, 0);
2423 argc = 0;
2424 if ((argv != NULL) && (argv[0] != NULL)) {
2425 while (argv[argc] != NULL)
2426 argc++;
2427
2428 status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2429 if (status < 0)
2430 WARNING("processes plugin: Command line did not fit into buffer.");
2431 else
2432 have_cmdline = 1;
2433 }
2434 } /* if (process has argument list) */
2435
2436 memset(&pse, 0, sizeof(pse));
2437 pse.id = procs[i].p_pid;
2438
2439 pse.num_proc = 1;
2440 pse.num_lwp = procs[i].p_nlwps;
2441
2442 pse.vmem_size = procs[i].p_uru_maxrss * pagesize;
2443 pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
2444 pse.vmem_data = procs[i].p_vm_dsize * pagesize;
2445 pse.vmem_code = procs[i].p_vm_tsize * pagesize;
2446 pse.stack_size = procs[i].p_vm_ssize * pagesize;
2447 pse.vmem_minflt_counter = procs[i].p_uru_minflt;
2448 pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2449
2450 pse.cpu_user_counter = 0;
2451 pse.cpu_system_counter = 0;
2452 /* context switch counters not implemented */
2453 pse.cswitch_vol = -1;
2454 pse.cswitch_invol = -1;
2455
2456 /*
2457 * The u-area might be swapped out, and we can't get
2458 * at it because we have a crashdump and no swap.
2459 * If it's here fill in these fields, otherwise, just
2460 * leave them 0.
2461 */
2462 if (procs[i].p_flag & P_INMEM) {
2463 pse.cpu_user_counter =
2464 procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2465 pse.cpu_system_counter =
2466 procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2467 }
2468
2469 /* no I/O data */
2470 pse.io_rchar = -1;
2471 pse.io_wchar = -1;
2472 pse.io_syscr = procs[i].p_uru_inblock;
2473 pse.io_syscw = procs[i].p_uru_oublock;
2474
2475 /* file descriptor count not implemented */
2476 pse.num_fd = 0;
2477
2478 /* Number of memory mappings */
2479 pse.num_maps = 0;
2480
2481 /* context switch counters not implemented */
2482 pse.cswitch_vol = -1;
2483 pse.cswitch_invol = -1;
2484
2485 ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2486 } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
2487
2488 /* system processes' LWPs end up in "running" state */
2489 if ((procs[i].p_flag & P_SYSTEM) != 0)
2490 continue;
2491
2492 switch (procs[i].p_realstat) {
2493 case SSTOP:
2494 case SACTIVE:
2495 case SIDL:
2496 p = &(procs[i]);
2497 /* get info about LWPs */
2498 kl = kvm_getlwps(kd, p->p_pid, (u_long)p->p_paddr,
2499 sizeof(struct kinfo_lwp), &nlwps);
2500
2501 for (l = 0; kl && l < nlwps; l++) {
2502 switch (kl[l].l_stat) {
2503 case LSONPROC:
2504 case LSRUN:
2505 running++;
2506 break;
2507 case LSSLEEP:
2508 if (kl[l].l_flag & L_SINTR) {
2509 if (kl[l].l_slptime > maxslp)
2510 idle++;
2511 else
2512 sleeping++;
2513 } else
2514 blocked++;
2515 break;
2516 case LSSTOP:
2517 stopped++;
2518 break;
2519 case LSIDL:
2520 idle++;
2521 break;
2522 }
2523 }
2524 break;
2525 case SZOMB:
2526 case SDYING:
2527 case SDEAD:
2528 zombies++;
2529 break;
2530 }
2531 }
2532
2533 kvm_close(kd);
2534
2535 ps_submit_state("running", running);
2536 ps_submit_state("sleeping", sleeping);
2537 ps_submit_state("zombies", zombies);
2538 ps_submit_state("stopped", stopped);
2539 ps_submit_state("blocked", blocked);
2540 ps_submit_state("idle", idle);
2541 ps_submit_state("wait", wait);
2542
2543 for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2544 ps_submit_proc_list(ps_ptr);
2545 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC2_NETBSD */
2546
2547 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
2548 int running = 0;
2549 int sleeping = 0;
2550 int zombies = 0;
2551 int stopped = 0;
2552 int onproc = 0;
2553 int idle = 0;
2554 int dead = 0;
2555
2556 kvm_t *kd;
2557 char errbuf[1024];
2558 struct kinfo_proc *procs; /* array of processes */
2559 struct kinfo_proc *proc_ptr = NULL;
2560 int count; /* returns number of processes */
2561
2562 process_entry_t pse;
2563
2564 ps_list_reset();
2565
2566 /* Open the kvm interface, get a descriptor */
2567 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
2568 if (kd == NULL) {
2569 ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2570 return 0;
2571 }
2572
2573 /* Get the list of processes. */
2574 procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
2575 if (procs == NULL) {
2576 ERROR("processes plugin: Cannot get kvm processes list: %s",
2577 kvm_geterr(kd));
2578 kvm_close(kd);
2579 return 0;
2580 }
2581
2582 /* Iterate through the processes in kinfo_proc */
2583 for (int i = 0; i < count; i++) {
2584 /* Create only one process list entry per _process_, i.e.
2585 * filter out threads (duplicate PID entries). */
2586 if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
2587 char cmdline[CMDLINE_BUFFER_SIZE] = "";
2588 bool have_cmdline = 0;
2589
2590 proc_ptr = &(procs[i]);
2591 /* Don't probe zombie processes */
2592 if (!P_ZOMBIE(proc_ptr)) {
2593 char **argv;
2594 int argc;
2595 int status;
2596
2597 /* retrieve the arguments */
2598 argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
2599 argc = 0;
2600 if ((argv != NULL) && (argv[0] != NULL)) {
2601 while (argv[argc] != NULL)
2602 argc++;
2603
2604 status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2605 if (status < 0)
2606 WARNING("processes plugin: Command line did not fit into buffer.");
2607 else
2608 have_cmdline = 1;
2609 }
2610 } /* if (process has argument list) */
2611
2612 memset(&pse, 0, sizeof(pse));
2613 pse.id = procs[i].p_pid;
2614
2615 pse.num_proc = 1;
2616 pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
2617
2618 pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
2619 pse.vmem_data = procs[i].p_vm_dsize * pagesize;
2620 pse.vmem_code = procs[i].p_vm_tsize * pagesize;
2621 pse.stack_size = procs[i].p_vm_ssize * pagesize;
2622 pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
2623 pse.vmem_minflt_counter = procs[i].p_uru_minflt;
2624 pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2625
2626 pse.cpu_user_counter =
2627 procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2628 pse.cpu_system_counter =
2629 procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2630
2631 /* no I/O data */
2632 pse.io_rchar = -1;
2633 pse.io_wchar = -1;
2634 pse.io_syscr = -1;
2635 pse.io_syscw = -1;
2636 pse.io_diskr = -1;
2637 pse.io_diskw = -1;
2638
2639 /* file descriptor count not implemented */
2640 pse.num_fd = 0;
2641
2642 /* Number of memory mappings */
2643 pse.num_maps = 0;
2644
2645 /* context switch counters not implemented */
2646 pse.cswitch_vol = -1;
2647 pse.cswitch_invol = -1;
2648
2649 ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2650
2651 switch (procs[i].p_stat) {
2652 case SSTOP:
2653 stopped++;
2654 break;
2655 case SSLEEP:
2656 sleeping++;
2657 break;
2658 case SRUN:
2659 running++;
2660 break;
2661 case SIDL:
2662 idle++;
2663 break;
2664 case SONPROC:
2665 onproc++;
2666 break;
2667 case SDEAD:
2668 dead++;
2669 break;
2670 case SZOMB:
2671 zombies++;
2672 break;
2673 }
2674 } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
2675 }
2676
2677 kvm_close(kd);
2678
2679 ps_submit_state("running", running);
2680 ps_submit_state("sleeping", sleeping);
2681 ps_submit_state("zombies", zombies);
2682 ps_submit_state("stopped", stopped);
2683 ps_submit_state("onproc", onproc);
2684 ps_submit_state("idle", idle);
2685 ps_submit_state("dead", dead);
2686
2687 for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2688 ps_submit_proc_list(ps_ptr);
2689 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
2690
2691 #elif HAVE_PROCINFO_H
2692 /* AIX */
2693 int running = 0;
2694 int sleeping = 0;
2695 int zombies = 0;
2696 int stopped = 0;
2697 int paging = 0;
2698 int blocked = 0;
2699
2700 pid_t pindex = 0;
2701 int nprocs;
2702
2703 process_entry_t pse;
2704
2705 ps_list_reset();
2706 while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
2707 /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
2708 &pindex, MAXPROCENTRY)) > 0) {
2709 for (int i = 0; i < nprocs; i++) {
2710 tid64_t thindex;
2711 int nthreads;
2712 char arglist[MAXARGLN + 1];
2713 char *cargs;
2714 char *cmdline;
2715
2716 if (procentry[i].pi_state == SNONE)
2717 continue;
2718 /* if (procentry[i].pi_state == SZOMB) FIXME */
2719
2720 cmdline = procentry[i].pi_comm;
2721 cargs = procentry[i].pi_comm;
2722 if (procentry[i].pi_flags & SKPROC) {
2723 if (procentry[i].pi_pid == 0)
2724 cmdline = "swapper";
2725 cargs = cmdline;
2726 } else {
2727 if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
2728 MAXARGLN) >= 0) {
2729 int n;
2730
2731 n = -1;
2732 while (++n < MAXARGLN) {
2733 if (arglist[n] == '\0') {
2734 if (arglist[n + 1] == '\0')
2735 break;
2736 arglist[n] = ' ';
2737 }
2738 }
2739 cargs = arglist;
2740 }
2741 }
2742
2743 memset(&pse, 0, sizeof(pse));
2744
2745 pse.id = procentry[i].pi_pid;
2746 pse.num_lwp = procentry[i].pi_thcount;
2747 pse.num_proc = 1;
2748
2749 thindex = 0;
2750 while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
2751 sizeof(struct thrdentry64), &thindex,
2752 MAXTHRDENTRY)) > 0) {
2753 int j;
2754
2755 for (j = 0; j < nthreads; j++) {
2756 switch (thrdentry[j].ti_state) {
2757 /* case TSNONE: break; */
2758 case TSIDL:
2759 blocked++;
2760 break; /* FIXME is really blocked */
2761 case TSRUN:
2762 running++;
2763 break;
2764 case TSSLEEP:
2765 sleeping++;
2766 break;
2767 case TSSWAP:
2768 paging++;
2769 break;
2770 case TSSTOP:
2771 stopped++;
2772 break;
2773 case TSZOMB:
2774 zombies++;
2775 break;
2776 }
2777 }
2778 if (nthreads < MAXTHRDENTRY)
2779 break;
2780 }
2781
2782 /* tv_usec is nanosec ??? */
2783 pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
2784 procentry[i].pi_ru.ru_utime.tv_usec / 1000;
2785
2786 /* tv_usec is nanosec ??? */
2787 pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
2788 procentry[i].pi_ru.ru_stime.tv_usec / 1000;
2789
2790 pse.vmem_minflt_counter = procentry[i].pi_minflt;
2791 pse.vmem_majflt_counter = procentry[i].pi_majflt;
2792
2793 pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
2794 pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
2795 /* Not supported/implemented */
2796 pse.vmem_data = 0;
2797 pse.vmem_code = 0;
2798 pse.stack_size = 0;
2799
2800 pse.io_rchar = -1;
2801 pse.io_wchar = -1;
2802 pse.io_syscr = -1;
2803 pse.io_syscw = -1;
2804 pse.io_diskr = -1;
2805 pse.io_diskw = -1;
2806
2807 pse.num_fd = 0;
2808 pse.num_maps = 0;
2809
2810 pse.cswitch_vol = -1;
2811 pse.cswitch_invol = -1;
2812
2813 ps_list_add(cmdline, cargs, &pse);
2814 } /* for (i = 0 .. nprocs) */
2815
2816 if (nprocs < MAXPROCENTRY)
2817 break;
2818 } /* while (getprocs64() > 0) */
2819 ps_submit_state("running", running);
2820 ps_submit_state("sleeping", sleeping);
2821 ps_submit_state("zombies", zombies);
2822 ps_submit_state("stopped", stopped);
2823 ps_submit_state("paging", paging);
2824 ps_submit_state("blocked", blocked);
2825
2826 for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
2827 ps_submit_proc_list(ps);
2828 /* #endif HAVE_PROCINFO_H */
2829
2830 #elif KERNEL_SOLARIS
2831 /*
2832 * The Solaris section adds a few more process states and removes some
2833 * process states compared to linux. Most notably there is no "PAGING"
2834 * and "BLOCKED" state for a process. The rest is similar to the linux
2835 * code.
2836 */
2837 int running = 0;
2838 int sleeping = 0;
2839 int zombies = 0;
2840 int stopped = 0;
2841 int detached = 0;
2842 int daemon = 0;
2843 int system = 0;
2844 int orphan = 0;
2845
2846 struct dirent *ent;
2847 DIR *proc;
2848
2849 int status;
2850 char state;
2851
2852 char cmdline[PRARGSZ];
2853
2854 ps_list_reset();
2855
2856 proc = opendir("/proc");
2857 if (proc == NULL)
2858 return -1;
2859
2860 while ((ent = readdir(proc)) != NULL) {
2861 long pid;
2862 process_entry_t pse;
2863 char *endptr;
2864
2865 if (!isdigit((int)ent->d_name[0]))
2866 continue;
2867
2868 pid = strtol(ent->d_name, &endptr, 10);
2869 if (*endptr != 0) /* value didn't completely parse as a number */
2870 continue;
2871
2872 memset(&pse, 0, sizeof(pse));
2873 pse.id = pid;
2874
2875 status = ps_read_process(pid, &pse, &state);
2876 if (status != 0) {
2877 DEBUG("ps_read_process failed: %i", status);
2878 continue;
2879 }
2880
2881 switch (state) {
2882 case 'R':
2883 running++;
2884 break;
2885 case 'S':
2886 sleeping++;
2887 break;
2888 case 'E':
2889 detached++;
2890 break;
2891 case 'Z':
2892 zombies++;
2893 break;
2894 case 'T':
2895 stopped++;
2896 break;
2897 case 'A':
2898 daemon++;
2899 break;
2900 case 'Y':
2901 system++;
2902 break;
2903 case 'O':
2904 orphan++;
2905 break;
2906 }
2907
2908 ps_list_add(pse.name,
2909 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
2910 } /* while(readdir) */
2911 closedir(proc);
2912
2913 ps_submit_state("running", running);
2914 ps_submit_state("sleeping", sleeping);
2915 ps_submit_state("zombies", zombies);
2916 ps_submit_state("stopped", stopped);
2917 ps_submit_state("detached", detached);
2918 ps_submit_state("daemon", daemon);
2919 ps_submit_state("system", system);
2920 ps_submit_state("orphan", orphan);
2921
2922 for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2923 ps_submit_proc_list(ps_ptr);
2924
2925 read_fork_rate();
2926 #endif /* KERNEL_SOLARIS */
2927
2928 want_init = false;
2929
2930 return 0;
2931 } /* int ps_read */
2932
2933 void module_register(void) {
2934 plugin_register_complex_config("processes", ps_config);
2935 plugin_register_init("processes", ps_init);
2936 plugin_register_read("processes", ps_read);
2937 } /* void module_register */
2938