1 /**
2 * collectd - src/processes.c
3 * Copyright (C) 2005 Lyonel Vincent
4 * Copyright (C) 2006-2010 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 **/
37 #include "collectd.h"
39 #include "common.h"
40 #include "plugin.h"
42 /* Include header files for the mach system, if they exist.. */
43 #if HAVE_THREAD_INFO
44 #if HAVE_MACH_MACH_INIT_H
45 #include <mach/mach_init.h>
46 #endif
47 #if HAVE_MACH_HOST_PRIV_H
48 #include <mach/host_priv.h>
49 #endif
50 #if HAVE_MACH_MACH_ERROR_H
51 #include <mach/mach_error.h>
52 #endif
53 #if HAVE_MACH_MACH_HOST_H
54 #include <mach/mach_host.h>
55 #endif
56 #if HAVE_MACH_MACH_PORT_H
57 #include <mach/mach_port.h>
58 #endif
59 #if HAVE_MACH_MACH_TYPES_H
60 #include <mach/mach_types.h>
61 #endif
62 #if HAVE_MACH_MESSAGE_H
63 #include <mach/message.h>
64 #endif
65 #if HAVE_MACH_PROCESSOR_SET_H
66 #include <mach/processor_set.h>
67 #endif
68 #if HAVE_MACH_TASK_H
69 #include <mach/task.h>
70 #endif
71 #if HAVE_MACH_THREAD_ACT_H
72 #include <mach/thread_act.h>
73 #endif
74 #if HAVE_MACH_VM_REGION_H
75 #include <mach/vm_region.h>
76 #endif
77 #if HAVE_MACH_VM_MAP_H
78 #include <mach/vm_map.h>
79 #endif
80 #if HAVE_MACH_VM_PROT_H
81 #include <mach/vm_prot.h>
82 #endif
83 #if HAVE_SYS_SYSCTL_H
84 #include <sys/sysctl.h>
85 #endif
86 /* #endif HAVE_THREAD_INFO */
88 #elif KERNEL_LINUX
89 #if HAVE_LINUX_CONFIG_H
90 #include <linux/config.h>
91 #endif
92 #ifndef CONFIG_HZ
93 #define CONFIG_HZ 100
94 #endif
95 /* #endif KERNEL_LINUX */
97 #elif HAVE_LIBKVM_GETPROCS && \
98 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
99 #include <kvm.h>
100 #include <sys/param.h>
101 #include <sys/proc.h>
102 #include <sys/sysctl.h>
103 #include <sys/user.h>
104 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
105 * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
107 #elif HAVE_PROCINFO_H
108 #include <procinfo.h>
109 #include <sys/types.h>
111 #define MAXPROCENTRY 32
112 #define MAXTHRDENTRY 16
113 #define MAXARGLN 1024
114 /* #endif HAVE_PROCINFO_H */
116 #elif KERNEL_SOLARIS
117 /* Hack: Avoid #error when building a 32-bit binary with
118 * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one
119 * of the structures in <sys/procfs.h> uses an off_t, but that
120 * isn't relevant to our usage of procfs. */
121 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
122 #define SAVE_FOB_64
123 #undef _FILE_OFFSET_BITS
124 #endif
126 #include <procfs.h>
128 #ifdef SAVE_FOB_64
129 #define _FILE_OFFSET_BITS 64
130 #undef SAVE_FOB_64
131 #endif
133 #include <dirent.h>
134 #include <sys/user.h>
136 #ifndef MAXCOMLEN
137 #define MAXCOMLEN 16
138 #endif
140 /* #endif KERNEL_SOLARIS */
142 #else
143 #error "No applicable input method."
144 #endif
146 #if HAVE_REGEX_H
147 #include <regex.h>
148 #endif
150 #if HAVE_KSTAT_H
151 #include <kstat.h>
152 #endif
154 #ifndef CMDLINE_BUFFER_SIZE
155 #if defined(ARG_MAX) && (ARG_MAX < 4096)
156 #define CMDLINE_BUFFER_SIZE ARG_MAX
157 #else
158 #define CMDLINE_BUFFER_SIZE 4096
159 #endif
160 #endif
162 typedef struct procstat_entry_s {
163 unsigned long id;
164 unsigned long age;
166 unsigned long num_proc;
167 unsigned long num_lwp;
168 unsigned long vmem_size;
169 unsigned long vmem_rss;
170 unsigned long vmem_data;
171 unsigned long vmem_code;
172 unsigned long stack_size;
174 unsigned long vmem_minflt;
175 unsigned long vmem_majflt;
176 derive_t vmem_minflt_counter;
177 derive_t vmem_majflt_counter;
179 unsigned long cpu_user;
180 unsigned long cpu_system;
181 derive_t cpu_user_counter;
182 derive_t cpu_system_counter;
184 /* io data */
185 derive_t io_rchar;
186 derive_t io_wchar;
187 derive_t io_syscr;
188 derive_t io_syscw;
190 derive_t cswitch_vol;
191 derive_t cswitch_invol;
193 struct procstat_entry_s *next;
194 } procstat_entry_t;
196 #define PROCSTAT_NAME_LEN 256
197 typedef struct procstat {
198 char name[PROCSTAT_NAME_LEN];
199 #if HAVE_REGEX_H
200 regex_t *re;
201 #endif
203 unsigned long num_proc;
204 unsigned long num_lwp;
205 unsigned long vmem_size;
206 unsigned long vmem_rss;
207 unsigned long vmem_data;
208 unsigned long vmem_code;
209 unsigned long stack_size;
211 derive_t vmem_minflt_counter;
212 derive_t vmem_majflt_counter;
214 derive_t cpu_user_counter;
215 derive_t cpu_system_counter;
217 /* io data */
218 derive_t io_rchar;
219 derive_t io_wchar;
220 derive_t io_syscr;
221 derive_t io_syscw;
223 derive_t cswitch_vol;
224 derive_t cswitch_invol;
226 struct procstat *next;
227 struct procstat_entry_s *instances;
228 } procstat_t;
230 static procstat_t *list_head_g = NULL;
232 static _Bool report_ctx_switch = 0;
234 #if HAVE_THREAD_INFO
235 static mach_port_t port_host_self;
236 static mach_port_t port_task_self;
238 static processor_set_name_array_t pset_list;
239 static mach_msg_type_number_t pset_list_len;
240 /* #endif HAVE_THREAD_INFO */
242 #elif KERNEL_LINUX
243 static long pagesize_g;
244 /* #endif KERNEL_LINUX */
246 #elif HAVE_LIBKVM_GETPROCS && \
247 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
248 static int pagesize;
249 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
250 * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
252 #elif HAVE_PROCINFO_H
253 static struct procentry64 procentry[MAXPROCENTRY];
254 static struct thrdentry64 thrdentry[MAXTHRDENTRY];
255 static int pagesize;
257 #ifndef _AIXVERSION_610
258 int getprocs64(void *procsinfo, int sizproc, void *fdsinfo, int sizfd,
259 pid_t *index, int count);
260 int getthrds64(pid_t, void *, int, tid64_t *, int);
261 #endif
262 int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
263 #endif /* HAVE_PROCINFO_H */
265 /* put name of process from config to list_head_g tree
266 * list_head_g is a list of 'procstat_t' structs with
267 * processes names we want to watch */
268 static void ps_list_register(const char *name, const char *regexp) {
269 procstat_t *new;
270 procstat_t *ptr;
271 int status;
273 new = calloc(1, sizeof(*new));
274 if (new == NULL) {
275 ERROR("processes plugin: ps_list_register: calloc failed.");
276 return;
277 }
278 sstrncpy(new->name, name, sizeof(new->name));
280 #if HAVE_REGEX_H
281 if (regexp != NULL) {
282 DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
283 name);
284 new->re = malloc(sizeof(*new->re));
285 if (new->re == NULL) {
286 ERROR("processes plugin: ps_list_register: malloc failed.");
287 sfree(new);
288 return;
289 }
291 status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
292 if (status != 0) {
293 DEBUG("ProcessMatch: compiling the regular expression \"%s\" failed.",
294 regexp);
295 sfree(new->re);
296 sfree(new);
297 return;
298 }
299 }
300 #else
301 if (regexp != NULL) {
302 ERROR("processes plugin: ps_list_register: "
303 "Regular expression \"%s\" found in config "
304 "file, but support for regular expressions "
305 "has been disabled at compile time.",
306 regexp);
307 sfree(new);
308 return;
309 }
310 #endif
312 for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) {
313 if (strcmp(ptr->name, name) == 0) {
314 WARNING("processes plugin: You have configured more "
315 "than one `Process' or "
316 "`ProcessMatch' with the same name. "
317 "All but the first setting will be "
318 "ignored.");
319 #if HAVE_REGEX_H
320 sfree(new->re);
321 #endif
322 sfree(new);
323 return;
324 }
326 if (ptr->next == NULL)
327 break;
328 }
330 if (ptr == NULL)
331 list_head_g = new;
332 else
333 ptr->next = new;
334 } /* void ps_list_register */
336 /* try to match name against entry, returns 1 if success */
337 static int ps_list_match(const char *name, const char *cmdline,
338 procstat_t *ps) {
339 #if HAVE_REGEX_H
340 if (ps->re != NULL) {
341 int status;
342 const char *str;
344 str = cmdline;
345 if ((str == NULL) || (str[0] == 0))
346 str = name;
348 assert(str != NULL);
350 status = regexec(ps->re, str,
351 /* nmatch = */ 0,
352 /* pmatch = */ NULL,
353 /* eflags = */ 0);
354 if (status == 0)
355 return (1);
356 } else
357 #endif
358 if (strcmp(ps->name, name) == 0)
359 return (1);
361 return (0);
362 } /* int ps_list_match */
364 static void ps_update_counter(_Bool init, derive_t *group_counter,
365 derive_t *curr_counter, unsigned long *curr_value,
366 derive_t new_counter, unsigned long new_value) {
367 if (init) {
368 *curr_value = new_value;
369 *curr_counter += new_value;
370 *group_counter += new_value;
371 return;
372 }
374 if (new_counter < *curr_counter)
375 *curr_value = new_counter + (ULONG_MAX - *curr_counter);
376 else
377 *curr_value = new_counter - *curr_counter;
379 *curr_counter = new_counter;
380 *group_counter += *curr_value;
381 }
383 /* add process entry to 'instances' of process 'name' (or refresh it) */
384 static void ps_list_add(const char *name, const char *cmdline,
385 procstat_entry_t *entry) {
386 procstat_entry_t *pse;
388 if (entry->id == 0)
389 return;
391 for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
392 _Bool want_init;
394 if ((ps_list_match(name, cmdline, ps)) == 0)
395 continue;
397 for (pse = ps->instances; pse != NULL; pse = pse->next)
398 if ((pse->id == entry->id) || (pse->next == NULL))
399 break;
401 if ((pse == NULL) || (pse->id != entry->id)) {
402 procstat_entry_t *new;
404 new = calloc(1, sizeof(*new));
405 if (new == NULL)
406 return;
407 new->id = entry->id;
409 if (pse == NULL)
410 ps->instances = new;
411 else
412 pse->next = new;
414 pse = new;
415 }
417 pse->age = 0;
418 pse->num_proc = entry->num_proc;
419 pse->num_lwp = entry->num_lwp;
420 pse->vmem_size = entry->vmem_size;
421 pse->vmem_rss = entry->vmem_rss;
422 pse->vmem_data = entry->vmem_data;
423 pse->vmem_code = entry->vmem_code;
424 pse->stack_size = entry->stack_size;
425 pse->io_rchar = entry->io_rchar;
426 pse->io_wchar = entry->io_wchar;
427 pse->io_syscr = entry->io_syscr;
428 pse->io_syscw = entry->io_syscw;
429 pse->cswitch_vol = entry->cswitch_vol;
430 pse->cswitch_invol = entry->cswitch_invol;
432 ps->num_proc += pse->num_proc;
433 ps->num_lwp += pse->num_lwp;
434 ps->vmem_size += pse->vmem_size;
435 ps->vmem_rss += pse->vmem_rss;
436 ps->vmem_data += pse->vmem_data;
437 ps->vmem_code += pse->vmem_code;
438 ps->stack_size += pse->stack_size;
440 ps->io_rchar += ((pse->io_rchar == -1) ? 0 : pse->io_rchar);
441 ps->io_wchar += ((pse->io_wchar == -1) ? 0 : pse->io_wchar);
442 ps->io_syscr += ((pse->io_syscr == -1) ? 0 : pse->io_syscr);
443 ps->io_syscw += ((pse->io_syscw == -1) ? 0 : pse->io_syscw);
445 ps->cswitch_vol += ((pse->cswitch_vol == -1) ? 0 : pse->cswitch_vol);
446 ps->cswitch_invol += ((pse->cswitch_invol == -1) ? 0 : pse->cswitch_invol);
448 want_init =
449 (entry->vmem_minflt_counter == 0) && (entry->vmem_majflt_counter == 0);
450 ps_update_counter(want_init, &ps->vmem_minflt_counter,
451 &pse->vmem_minflt_counter, &pse->vmem_minflt,
452 entry->vmem_minflt_counter, entry->vmem_minflt);
453 ps_update_counter(want_init, &ps->vmem_majflt_counter,
454 &pse->vmem_majflt_counter, &pse->vmem_majflt,
455 entry->vmem_majflt_counter, entry->vmem_majflt);
457 want_init =
458 (entry->cpu_user_counter == 0) && (entry->cpu_system_counter == 0);
459 ps_update_counter(want_init, &ps->cpu_user_counter, &pse->cpu_user_counter,
460 &pse->cpu_user, entry->cpu_user_counter, entry->cpu_user);
461 ps_update_counter(want_init, &ps->cpu_system_counter,
462 &pse->cpu_system_counter, &pse->cpu_system,
463 entry->cpu_system_counter, entry->cpu_system);
464 }
465 }
467 /* remove old entries from instances of processes in list_head_g */
468 static void ps_list_reset(void) {
469 procstat_entry_t *pse;
470 procstat_entry_t *pse_prev;
472 for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
473 ps->num_proc = 0;
474 ps->num_lwp = 0;
475 ps->vmem_size = 0;
476 ps->vmem_rss = 0;
477 ps->vmem_data = 0;
478 ps->vmem_code = 0;
479 ps->stack_size = 0;
480 ps->io_rchar = -1;
481 ps->io_wchar = -1;
482 ps->io_syscr = -1;
483 ps->io_syscw = -1;
484 ps->cswitch_vol = -1;
485 ps->cswitch_invol = -1;
487 pse_prev = NULL;
488 pse = ps->instances;
489 while (pse != NULL) {
490 if (pse->age > 10) {
491 DEBUG("Removing this procstat entry cause it's too old: "
492 "id = %lu; name = %s;",
493 pse->id, ps->name);
495 if (pse_prev == NULL) {
496 ps->instances = pse->next;
497 free(pse);
498 pse = ps->instances;
499 } else {
500 pse_prev->next = pse->next;
501 free(pse);
502 pse = pse_prev->next;
503 }
504 } else {
505 pse->age++;
506 pse_prev = pse;
507 pse = pse->next;
508 }
509 } /* while (pse != NULL) */
510 } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
511 }
513 /* put all pre-defined 'Process' names from config to list_head_g tree */
514 static int ps_config(oconfig_item_t *ci) {
515 #if KERNEL_LINUX
516 const size_t max_procname_len = 15;
517 #elif KERNEL_SOLARIS || KERNEL_FREEBSD
518 const size_t max_procname_len = MAXCOMLEN - 1;
519 #endif
521 for (int i = 0; i < ci->children_num; ++i) {
522 oconfig_item_t *c = ci->children + i;
524 if (strcasecmp(c->key, "Process") == 0) {
525 if ((c->values_num != 1) || (OCONFIG_TYPE_STRING != c->values[0].type)) {
526 ERROR("processes plugin: `Process' expects exactly "
527 "one string argument (got %i).",
528 c->values_num);
529 continue;
530 }
532 if (c->children_num != 0) {
533 WARNING("processes plugin: the `Process' config option "
534 "does not expect any child elements -- ignoring "
535 "content (%i elements) of the <Process '%s'> block.",
536 c->children_num, c->values[0].value.string);
537 }
539 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
540 if (strlen(c->values[0].value.string) > max_procname_len) {
541 WARNING("processes plugin: this platform has a %zu character limit "
542 "to process names. The `Process \"%s\"' option will "
543 "not work as expected.",
544 max_procname_len, c->values[0].value.string);
545 }
546 #endif
548 ps_list_register(c->values[0].value.string, NULL);
549 } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
550 if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
551 (OCONFIG_TYPE_STRING != c->values[1].type)) {
552 ERROR("processes plugin: `ProcessMatch' needs exactly "
553 "two string arguments (got %i).",
554 c->values_num);
555 continue;
556 }
558 if (c->children_num != 0) {
559 WARNING("processes plugin: the `ProcessMatch' config option "
560 "does not expect any child elements -- ignoring "
561 "content (%i elements) of the <ProcessMatch '%s' '%s'> "
562 "block.",
563 c->children_num, c->values[0].value.string,
564 c->values[1].value.string);
565 }
567 ps_list_register(c->values[0].value.string, c->values[1].value.string);
568 } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
569 cf_util_get_boolean(c, &report_ctx_switch);
570 } else {
571 ERROR("processes plugin: The `%s' configuration option is not "
572 "understood and will be ignored.",
573 c->key);
574 continue;
575 }
576 }
578 return (0);
579 }
581 static int ps_init(void) {
582 #if HAVE_THREAD_INFO
583 kern_return_t status;
585 port_host_self = mach_host_self();
586 port_task_self = mach_task_self();
588 if (pset_list != NULL) {
589 vm_deallocate(port_task_self, (vm_address_t)pset_list,
590 pset_list_len * sizeof(processor_set_t));
591 pset_list = NULL;
592 pset_list_len = 0;
593 }
595 if ((status = host_processor_sets(port_host_self, &pset_list,
596 &pset_list_len)) != KERN_SUCCESS) {
597 ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
598 pset_list = NULL;
599 pset_list_len = 0;
600 return (-1);
601 }
602 /* #endif HAVE_THREAD_INFO */
604 #elif KERNEL_LINUX
605 pagesize_g = sysconf(_SC_PAGESIZE);
606 DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
607 /* #endif KERNEL_LINUX */
609 #elif HAVE_LIBKVM_GETPROCS && \
610 (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
611 pagesize = getpagesize();
612 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
613 * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
615 #elif HAVE_PROCINFO_H
616 pagesize = getpagesize();
617 #endif /* HAVE_PROCINFO_H */
619 return (0);
620 } /* int ps_init */
622 /* submit global state (e.g.: qty of zombies, running, etc..) */
623 static void ps_submit_state(const char *state, double value) {
624 value_t values[1];
625 value_list_t vl = VALUE_LIST_INIT;
627 values[0].gauge = value;
629 vl.values = values;
630 vl.values_len = 1;
631 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
632 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
633 sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
634 sstrncpy(vl.type, "ps_state", sizeof(vl.type));
635 sstrncpy(vl.type_instance, state, sizeof(vl.type_instance));
637 plugin_dispatch_values(&vl);
638 }
640 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
641 static void ps_submit_proc_list(procstat_t *ps) {
642 value_t values[2];
643 value_list_t vl = VALUE_LIST_INIT;
645 vl.values = values;
646 vl.values_len = 2;
647 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
648 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
649 sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
651 sstrncpy(vl.type, "ps_vm", sizeof(vl.type));
652 vl.values[0].gauge = ps->vmem_size;
653 vl.values_len = 1;
654 plugin_dispatch_values(&vl);
656 sstrncpy(vl.type, "ps_rss", sizeof(vl.type));
657 vl.values[0].gauge = ps->vmem_rss;
658 vl.values_len = 1;
659 plugin_dispatch_values(&vl);
661 sstrncpy(vl.type, "ps_data", sizeof(vl.type));
662 vl.values[0].gauge = ps->vmem_data;
663 vl.values_len = 1;
664 plugin_dispatch_values(&vl);
666 sstrncpy(vl.type, "ps_code", sizeof(vl.type));
667 vl.values[0].gauge = ps->vmem_code;
668 vl.values_len = 1;
669 plugin_dispatch_values(&vl);
671 sstrncpy(vl.type, "ps_stacksize", sizeof(vl.type));
672 vl.values[0].gauge = ps->stack_size;
673 vl.values_len = 1;
674 plugin_dispatch_values(&vl);
676 sstrncpy(vl.type, "ps_cputime", sizeof(vl.type));
677 vl.values[0].derive = ps->cpu_user_counter;
678 vl.values[1].derive = ps->cpu_system_counter;
679 vl.values_len = 2;
680 plugin_dispatch_values(&vl);
682 sstrncpy(vl.type, "ps_count", sizeof(vl.type));
683 vl.values[0].gauge = ps->num_proc;
684 vl.values[1].gauge = ps->num_lwp;
685 vl.values_len = 2;
686 plugin_dispatch_values(&vl);
688 sstrncpy(vl.type, "ps_pagefaults", sizeof(vl.type));
689 vl.values[0].derive = ps->vmem_minflt_counter;
690 vl.values[1].derive = ps->vmem_majflt_counter;
691 vl.values_len = 2;
692 plugin_dispatch_values(&vl);
694 if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
695 sstrncpy(vl.type, "ps_disk_octets", sizeof(vl.type));
696 vl.values[0].derive = ps->io_rchar;
697 vl.values[1].derive = ps->io_wchar;
698 vl.values_len = 2;
699 plugin_dispatch_values(&vl);
700 }
702 if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
703 sstrncpy(vl.type, "ps_disk_ops", sizeof(vl.type));
704 vl.values[0].derive = ps->io_syscr;
705 vl.values[1].derive = ps->io_syscw;
706 vl.values_len = 2;
707 plugin_dispatch_values(&vl);
708 }
710 if (report_ctx_switch) {
711 sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
712 sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
713 vl.values[0].derive = ps->cswitch_vol;
714 vl.values_len = 1;
715 plugin_dispatch_values(&vl);
717 sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
718 sstrncpy(vl.type_instance, "involuntary", sizeof(vl.type_instance));
719 vl.values[0].derive = ps->cswitch_invol;
720 vl.values_len = 1;
721 plugin_dispatch_values(&vl);
722 }
724 DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; "
725 "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
726 "vmem_code = %lu; "
727 "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
728 "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
729 "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
730 "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
731 "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
732 ps->name, ps->num_proc, ps->num_lwp, ps->vmem_size, ps->vmem_rss,
733 ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
734 ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter,
735 ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->cswitch_vol,
736 ps->cswitch_invol);
737 } /* void ps_submit_proc_list */
739 #if KERNEL_LINUX || KERNEL_SOLARIS
740 static void ps_submit_fork_rate(derive_t value) {
741 value_t values[1];
742 value_list_t vl = VALUE_LIST_INIT;
744 values[0].derive = value;
746 vl.values = values;
747 vl.values_len = 1;
748 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
749 sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
750 sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
751 sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
752 sstrncpy(vl.type_instance, "", sizeof(vl.type_instance));
754 plugin_dispatch_values(&vl);
755 }
756 #endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
758 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
759 #if KERNEL_LINUX
760 static procstat_t *ps_read_tasks_status(long pid, procstat_t *ps) {
761 char dirname[64];
762 DIR *dh;
763 char filename[64];
764 FILE *fh;
765 struct dirent *ent;
766 derive_t cswitch_vol = 0;
767 derive_t cswitch_invol = 0;
768 char buffer[1024];
769 char *fields[8];
770 int numfields;
772 ssnprintf(dirname, sizeof(dirname), "/proc/%li/task", pid);
774 if ((dh = opendir(dirname)) == NULL) {
775 DEBUG("Failed to open directory `%s'", dirname);
776 return (NULL);
777 }
779 while ((ent = readdir(dh)) != NULL) {
780 char *tpid;
782 if (!isdigit((int)ent->d_name[0]))
783 continue;
785 tpid = ent->d_name;
787 ssnprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", pid,
788 tpid);
789 if ((fh = fopen(filename, "r")) == NULL) {
790 DEBUG("Failed to open file `%s'", filename);
791 continue;
792 }
794 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
795 derive_t tmp;
796 char *endptr;
798 if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
799 strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
800 continue;
802 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
804 if (numfields < 2)
805 continue;
807 errno = 0;
808 endptr = NULL;
809 tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
810 if ((errno == 0) && (endptr != fields[1])) {
811 if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
812 cswitch_vol += tmp;
813 } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
814 cswitch_invol += tmp;
815 }
816 }
817 } /* while (fgets) */
819 if (fclose(fh)) {
820 char errbuf[1024];
821 WARNING("processes: fclose: %s",
822 sstrerror(errno, errbuf, sizeof(errbuf)));
823 }
824 }
825 closedir(dh);
827 ps->cswitch_vol = cswitch_vol;
828 ps->cswitch_invol = cswitch_invol;
830 return (ps);
831 } /* int *ps_read_tasks_status */
833 /* Read data from /proc/pid/status */
834 static procstat_t *ps_read_status(long pid, procstat_t *ps) {
835 FILE *fh;
836 char buffer[1024];
837 char filename[64];
838 unsigned long lib = 0;
839 unsigned long exe = 0;
840 unsigned long data = 0;
841 unsigned long threads = 0;
842 char *fields[8];
843 int numfields;
845 ssnprintf(filename, sizeof(filename), "/proc/%li/status", pid);
846 if ((fh = fopen(filename, "r")) == NULL)
847 return (NULL);
849 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
850 unsigned long tmp;
851 char *endptr;
853 if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
854 continue;
856 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
858 if (numfields < 2)
859 continue;
861 errno = 0;
862 endptr = NULL;
863 tmp = strtoul(fields[1], &endptr, /* base = */ 10);
864 if ((errno == 0) && (endptr != fields[1])) {
865 if (strncmp(buffer, "VmData", 6) == 0) {
866 data = tmp;
867 } else if (strncmp(buffer, "VmLib", 5) == 0) {
868 lib = tmp;
869 } else if (strncmp(buffer, "VmExe", 5) == 0) {
870 exe = tmp;
871 } else if (strncmp(buffer, "Threads", 7) == 0) {
872 threads = tmp;
873 }
874 }
875 } /* while (fgets) */
877 if (fclose(fh)) {
878 char errbuf[1024];
879 WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
880 }
882 ps->vmem_data = data * 1024;
883 ps->vmem_code = (exe + lib) * 1024;
884 if (threads != 0)
885 ps->num_lwp = threads;
887 return (ps);
888 } /* procstat_t *ps_read_vmem */
890 static procstat_t *ps_read_io(long pid, procstat_t *ps) {
891 FILE *fh;
892 char buffer[1024];
893 char filename[64];
895 char *fields[8];
896 int numfields;
898 ssnprintf(filename, sizeof(filename), "/proc/%li/io", pid);
899 if ((fh = fopen(filename, "r")) == NULL)
900 return (NULL);
902 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
903 derive_t *val = NULL;
904 long long tmp;
905 char *endptr;
907 if (strncasecmp(buffer, "rchar:", 6) == 0)
908 val = &(ps->io_rchar);
909 else if (strncasecmp(buffer, "wchar:", 6) == 0)
910 val = &(ps->io_wchar);
911 else if (strncasecmp(buffer, "syscr:", 6) == 0)
912 val = &(ps->io_syscr);
913 else if (strncasecmp(buffer, "syscw:", 6) == 0)
914 val = &(ps->io_syscw);
915 else
916 continue;
918 numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
920 if (numfields < 2)
921 continue;
923 errno = 0;
924 endptr = NULL;
925 tmp = strtoll(fields[1], &endptr, /* base = */ 10);
926 if ((errno != 0) || (endptr == fields[1]))
927 *val = -1;
928 else
929 *val = (derive_t)tmp;
930 } /* while (fgets) */
932 if (fclose(fh)) {
933 char errbuf[1024];
934 WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
935 }
937 return (ps);
938 } /* procstat_t *ps_read_io */
940 static int ps_read_process(long pid, procstat_t *ps, char *state) {
941 char filename[64];
942 char buffer[1024];
944 char *fields[64];
945 char fields_len;
947 size_t buffer_len;
949 char *buffer_ptr;
950 size_t name_start_pos;
951 size_t name_end_pos;
952 size_t name_len;
954 derive_t cpu_user_counter;
955 derive_t cpu_system_counter;
956 long long unsigned vmem_size;
957 long long unsigned vmem_rss;
958 long long unsigned stack_size;
960 ssize_t status;
962 memset(ps, 0, sizeof(procstat_t));
964 ssnprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
966 status = read_file_contents(filename, buffer, sizeof(buffer) - 1);
967 if (status <= 0)
968 return (-1);
969 buffer_len = (size_t)status;
970 buffer[buffer_len] = 0;
972 /* The name of the process is enclosed in parens. Since the name can
973 * contain parens itself, spaces, numbers and pretty much everything
974 * else, use these to determine the process name. We don't use
975 * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
976 * otherwise be required to determine name_len. */
977 name_start_pos = 0;
978 while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
979 name_start_pos++;
981 name_end_pos = buffer_len;
982 while (name_end_pos > 0 && buffer[name_end_pos] != ')')
983 name_end_pos--;
985 /* Either '(' or ')' is not found or they are in the wrong order.
986 * Anyway, something weird that shouldn't happen ever. */
987 if (name_start_pos >= name_end_pos) {
988 ERROR("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
989 name_start_pos, name_end_pos);
990 return (-1);
991 }
993 name_len = (name_end_pos - name_start_pos) - 1;
994 if (name_len >= sizeof(ps->name))
995 name_len = sizeof(ps->name) - 1;
997 sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
999 if ((buffer_len - name_end_pos) < 2)
1000 return (-1);
1001 buffer_ptr = &buffer[name_end_pos + 2];
1003 fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
1004 if (fields_len < 22) {
1005 DEBUG("processes plugin: ps_read_process (pid = %li):"
1006 " `%s' has only %i fields..",
1007 pid, filename, fields_len);
1008 return (-1);
1009 }
1011 *state = fields[0][0];
1013 if (*state == 'Z') {
1014 ps->num_lwp = 0;
1015 ps->num_proc = 0;
1016 } else {
1017 ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
1018 if ((ps_read_status(pid, ps)) == NULL) {
1019 /* No VMem data */
1020 ps->vmem_data = -1;
1021 ps->vmem_code = -1;
1022 DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
1023 }
1024 if (ps->num_lwp == 0)
1025 ps->num_lwp = 1;
1026 ps->num_proc = 1;
1027 }
1029 /* Leave the rest at zero if this is only a zombi */
1030 if (ps->num_proc == 0) {
1031 DEBUG("processes plugin: This is only a zombie: pid = %li; "
1032 "name = %s;",
1033 pid, ps->name);
1034 return (0);
1035 }
1037 cpu_user_counter = atoll(fields[11]);
1038 cpu_system_counter = atoll(fields[12]);
1039 vmem_size = atoll(fields[20]);
1040 vmem_rss = atoll(fields[21]);
1041 ps->vmem_minflt_counter = atol(fields[7]);
1042 ps->vmem_majflt_counter = atol(fields[9]);
1044 {
1045 unsigned long long stack_start = atoll(fields[25]);
1046 unsigned long long stack_ptr = atoll(fields[26]);
1048 stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
1049 : stack_ptr - stack_start;
1050 }
1052 /* Convert jiffies to useconds */
1053 cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
1054 cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
1055 vmem_rss = vmem_rss * pagesize_g;
1057 ps->cpu_user_counter = cpu_user_counter;
1058 ps->cpu_system_counter = cpu_system_counter;
1059 ps->vmem_size = (unsigned long)vmem_size;
1060 ps->vmem_rss = (unsigned long)vmem_rss;
1061 ps->stack_size = (unsigned long)stack_size;
1063 if ((ps_read_io(pid, ps)) == NULL) {
1064 /* no io data */
1065 ps->io_rchar = -1;
1066 ps->io_wchar = -1;
1067 ps->io_syscr = -1;
1068 ps->io_syscw = -1;
1070 DEBUG("ps_read_process: not get io data for pid %li", pid);
1071 }
1073 if (report_ctx_switch) {
1074 if ((ps_read_tasks_status(pid, ps)) == NULL) {
1075 ps->cswitch_vol = -1;
1076 ps->cswitch_invol = -1;
1078 DEBUG("ps_read_tasks_status: not get context "
1079 "switch data for pid %li",
1080 pid);
1081 }
1082 }
1084 /* success */
1085 return (0);
1086 } /* int ps_read_process (...) */
1088 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
1089 char *buf_ptr;
1090 size_t len;
1092 char file[PATH_MAX];
1093 int fd;
1095 size_t n;
1097 if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1098 return NULL;
1100 ssnprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
1102 errno = 0;
1103 fd = open(file, O_RDONLY);
1104 if (fd < 0) {
1105 char errbuf[4096];
1106 /* ENOENT means the process exited while we were handling it.
1107 * Don't complain about this, it only fills the logs. */
1108 if (errno != ENOENT)
1109 WARNING("processes plugin: Failed to open `%s': %s.", file,
1110 sstrerror(errno, errbuf, sizeof(errbuf)));
1111 return NULL;
1112 }
1114 buf_ptr = buf;
1115 len = buf_len;
1117 n = 0;
1119 while (42) {
1120 ssize_t status;
1122 status = read(fd, (void *)buf_ptr, len);
1124 if (status < 0) {
1125 char errbuf[1024];
1127 if ((EAGAIN == errno) || (EINTR == errno))
1128 continue;
1130 WARNING("processes plugin: Failed to read from `%s': %s.", file,
1131 sstrerror(errno, errbuf, sizeof(errbuf)));
1132 close(fd);
1133 return NULL;
1134 }
1136 n += status;
1138 if (status == 0)
1139 break;
1141 buf_ptr += status;
1142 len -= status;
1144 if (len == 0)
1145 break;
1146 }
1148 close(fd);
1150 if (0 == n) {
1151 /* cmdline not available; e.g. kernel thread, zombie */
1152 if (NULL == name)
1153 return NULL;
1155 ssnprintf(buf, buf_len, "[%s]", name);
1156 return buf;
1157 }
1159 assert(n <= buf_len);
1161 if (n == buf_len)
1162 --n;
1163 buf[n] = '\0';
1165 --n;
1166 /* remove trailing whitespace */
1167 while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
1168 buf[n] = '\0';
1169 --n;
1170 }
1172 /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1173 while (n > 0) {
1174 if ('\0' == buf[n])
1175 buf[n] = ' ';
1176 --n;
1177 }
1178 return buf;
1179 } /* char *ps_get_cmdline (...) */
1181 static int read_fork_rate(void) {
1182 FILE *proc_stat;
1183 char buffer[1024];
1184 value_t value;
1185 _Bool value_valid = 0;
1187 proc_stat = fopen("/proc/stat", "r");
1188 if (proc_stat == NULL) {
1189 char errbuf[1024];
1190 ERROR("processes plugin: fopen (/proc/stat) failed: %s",
1191 sstrerror(errno, errbuf, sizeof(errbuf)));
1192 return (-1);
1193 }
1195 while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
1196 int status;
1197 char *fields[3];
1198 int fields_num;
1200 fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1201 if (fields_num != 2)
1202 continue;
1204 if (strcmp("processes", fields[0]) != 0)
1205 continue;
1207 status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
1208 if (status == 0)
1209 value_valid = 1;
1211 break;
1212 }
1213 fclose(proc_stat);
1215 if (!value_valid)
1216 return (-1);
1218 ps_submit_fork_rate(value.derive);
1219 return (0);
1220 }
1221 #endif /*KERNEL_LINUX */
1223 #if KERNEL_SOLARIS
1224 static char *ps_get_cmdline(long pid,
1225 char *name __attribute__((unused)), /* {{{ */
1226 char *buffer, size_t buffer_size) {
1227 char path[PATH_MAX];
1228 psinfo_t info;
1229 ssize_t status;
1231 snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
1233 status = read_file_contents(path, (void *)&info, sizeof(info));
1234 if ((status < 0) || (((size_t)status) != sizeof(info))) {
1235 ERROR("processes plugin: Unexpected return value "
1236 "while reading \"%s\": "
1237 "Returned %zd but expected %zu.",
1238 path, status, buffer_size);
1239 return (NULL);
1240 }
1242 info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
1243 sstrncpy(buffer, info.pr_psargs, buffer_size);
1245 return (buffer);
1246 } /* }}} int ps_get_cmdline */
1248 /*
1249 * Reads process information on the Solaris OS. The information comes mainly
1250 * from
1251 * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
1252 * The values for input and ouput chars are calculated "by hand"
1253 * Added a few "solaris" specific process states as well
1254 */
1255 static int ps_read_process(long pid, procstat_t *ps, char *state) {
1256 char filename[64];
1257 char f_psinfo[64], f_usage[64];
1258 char *buffer;
1260 pstatus_t *myStatus;
1261 psinfo_t *myInfo;
1262 prusage_t *myUsage;
1264 snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1265 snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
1266 snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
1268 buffer = calloc(1, sizeof(pstatus_t));
1269 read_file_contents(filename, buffer, sizeof(pstatus_t));
1270 myStatus = (pstatus_t *)buffer;
1272 buffer = calloc(1, sizeof(psinfo_t));
1273 read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
1274 myInfo = (psinfo_t *)buffer;
1276 buffer = calloc(1, sizeof(prusage_t));
1277 read_file_contents(f_usage, buffer, sizeof(prusage_t));
1278 myUsage = (prusage_t *)buffer;
1280 sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
1281 ps->num_lwp = myStatus->pr_nlwp;
1282 if (myInfo->pr_wstat != 0) {
1283 ps->num_proc = 0;
1284 ps->num_lwp = 0;
1285 *state = (char)'Z';
1287 sfree(myStatus);
1288 sfree(myInfo);
1289 sfree(myUsage);
1290 return (0);
1291 } else {
1292 ps->num_proc = 1;
1293 ps->num_lwp = myInfo->pr_nlwp;
1294 }
1296 /*
1297 * Convert system time and user time from nanoseconds to microseconds
1298 * for compatibility with the linux module
1299 */
1300 ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
1301 ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
1303 /*
1304 * Convert rssize from KB to bytes to be consistent w/ the linux module
1305 */
1306 ps->vmem_rss = myInfo->pr_rssize * 1024;
1307 ps->vmem_size = myInfo->pr_size * 1024;
1308 ps->vmem_minflt_counter = myUsage->pr_minf;
1309 ps->vmem_majflt_counter = myUsage->pr_majf;
1311 /*
1312 * TODO: Data and code segment calculations for Solaris
1313 */
1315 ps->vmem_data = -1;
1316 ps->vmem_code = -1;
1317 ps->stack_size = myStatus->pr_stksize;
1319 /*
1320 * Calculating input/ouput chars
1321 * Formula used is total chars / total blocks => chars/block
1322 * then convert input/output blocks to chars
1323 */
1324 ulong_t tot_chars = myUsage->pr_ioch;
1325 ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
1326 ulong_t chars_per_block = 1;
1327 if (tot_blocks != 0)
1328 chars_per_block = tot_chars / tot_blocks;
1329 ps->io_rchar = myUsage->pr_inblk * chars_per_block;
1330 ps->io_wchar = myUsage->pr_oublk * chars_per_block;
1331 ps->io_syscr = myUsage->pr_sysc;
1332 ps->io_syscw = myUsage->pr_sysc;
1334 /*
1335 * TODO: context switch counters for Solaris
1336 */
1337 ps->cswitch_vol = -1;
1338 ps->cswitch_invol = -1;
1340 /*
1341 * TODO: Find way of setting BLOCKED and PAGING status
1342 */
1344 *state = (char)'R';
1345 if (myStatus->pr_flags & PR_ASLEEP)
1346 *state = (char)'S';
1347 else if (myStatus->pr_flags & PR_STOPPED)
1348 *state = (char)'T';
1349 else if (myStatus->pr_flags & PR_DETACH)
1350 *state = (char)'E';
1351 else if (myStatus->pr_flags & PR_DAEMON)
1352 *state = (char)'A';
1353 else if (myStatus->pr_flags & PR_ISSYS)
1354 *state = (char)'Y';
1355 else if (myStatus->pr_flags & PR_ORPHAN)
1356 *state = (char)'O';
1358 sfree(myStatus);
1359 sfree(myInfo);
1360 sfree(myUsage);
1362 return (0);
1363 }
1365 /*
1366 * Reads the number of threads created since the last reboot. On Solaris these
1367 * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
1368 * The result is the sum for all the threads created on each cpu
1369 */
1370 static int read_fork_rate(void) {
1371 extern kstat_ctl_t *kc;
1372 derive_t result = 0;
1374 if (kc == NULL)
1375 return (-1);
1377 for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
1378 ksp_chain = ksp_chain->ks_next) {
1379 if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
1380 (strcmp(ksp_chain->ks_name, "sys") == 0) &&
1381 (strcmp(ksp_chain->ks_class, "misc") == 0)) {
1382 long long tmp;
1384 kstat_read(kc, ksp_chain, NULL);
1386 tmp = get_kstat_value(ksp_chain, "nthreads");
1387 if (tmp != -1LL)
1388 result += tmp;
1389 }
1390 }
1392 ps_submit_fork_rate(result);
1393 return (0);
1394 }
1395 #endif /* KERNEL_SOLARIS */
1397 #if HAVE_THREAD_INFO
1398 static int mach_get_task_name(task_t t, int *pid, char *name,
1399 size_t name_max_len) {
1400 int mib[4];
1402 struct kinfo_proc kp;
1403 size_t kp_size;
1405 mib[0] = CTL_KERN;
1406 mib[1] = KERN_PROC;
1407 mib[2] = KERN_PROC_PID;
1409 if (pid_for_task(t, pid) != KERN_SUCCESS)
1410 return (-1);
1411 mib[3] = *pid;
1413 kp_size = sizeof(kp);
1414 if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
1415 return (-1);
1417 if (name_max_len > (MAXCOMLEN + 1))
1418 name_max_len = MAXCOMLEN + 1;
1420 strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
1421 name[name_max_len - 1] = '\0';
1423 DEBUG("pid = %i; name = %s;", *pid, name);
1425 /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1426 * `top' does it, because it is a lot of work and only used when
1427 * debugging. -octo */
1429 return (0);
1430 }
1431 #endif /* HAVE_THREAD_INFO */
1432 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
1433 */
1435 /* do actual readings from kernel */
1436 static int ps_read(void) {
1437 #if HAVE_THREAD_INFO
1438 kern_return_t status;
1440 processor_set_t port_pset_priv;
1442 task_array_t task_list;
1443 mach_msg_type_number_t task_list_len;
1445 int task_pid;
1446 char task_name[MAXCOMLEN + 1];
1448 thread_act_array_t thread_list;
1449 mach_msg_type_number_t thread_list_len;
1450 thread_basic_info_data_t thread_data;
1451 mach_msg_type_number_t thread_data_len;
1453 int running = 0;
1454 int sleeping = 0;
1455 int zombies = 0;
1456 int stopped = 0;
1457 int blocked = 0;
1459 procstat_t *ps;
1460 procstat_entry_t pse;
1462 ps_list_reset();
1464 /*
1465 * The Mach-concept is a little different from the traditional UNIX
1466 * concept: All the work is done in threads. Threads are contained in
1467 * `tasks'. Therefore, `task status' doesn't make much sense, since
1468 * it's actually a `thread status'.
1469 * Tasks are assigned to sets of processors, so that's where you go to
1470 * get a list.
1471 */
1472 for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
1473 if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
1474 &port_pset_priv)) != KERN_SUCCESS) {
1475 ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
1476 continue;
1477 }
1479 if ((status = processor_set_tasks(port_pset_priv, &task_list,
1480 &task_list_len)) != KERN_SUCCESS) {
1481 ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
1482 mach_port_deallocate(port_task_self, port_pset_priv);
1483 continue;
1484 }
1486 for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
1487 ps = NULL;
1488 if (mach_get_task_name(task_list[task], &task_pid, task_name,
1489 PROCSTAT_NAME_LEN) == 0) {
1490 /* search for at least one match */
1491 for (ps = list_head_g; ps != NULL; ps = ps->next)
1492 /* FIXME: cmdline should be here instead of NULL */
1493 if (ps_list_match(task_name, NULL, ps) == 1)
1494 break;
1495 }
1497 /* Collect more detailed statistics for this process */
1498 if (ps != NULL) {
1499 task_basic_info_data_t task_basic_info;
1500 mach_msg_type_number_t task_basic_info_len;
1501 task_events_info_data_t task_events_info;
1502 mach_msg_type_number_t task_events_info_len;
1503 task_absolutetime_info_data_t task_absolutetime_info;
1504 mach_msg_type_number_t task_absolutetime_info_len;
1506 memset(&pse, '\0', sizeof(pse));
1507 pse.id = task_pid;
1509 task_basic_info_len = TASK_BASIC_INFO_COUNT;
1510 status = task_info(task_list[task], TASK_BASIC_INFO,
1511 (task_info_t)&task_basic_info, &task_basic_info_len);
1512 if (status != KERN_SUCCESS) {
1513 ERROR("task_info failed: %s", mach_error_string(status));
1514 continue; /* with next thread_list */
1515 }
1517 task_events_info_len = TASK_EVENTS_INFO_COUNT;
1518 status =
1519 task_info(task_list[task], TASK_EVENTS_INFO,
1520 (task_info_t)&task_events_info, &task_events_info_len);
1521 if (status != KERN_SUCCESS) {
1522 ERROR("task_info failed: %s", mach_error_string(status));
1523 continue; /* with next thread_list */
1524 }
1526 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1527 status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
1528 (task_info_t)&task_absolutetime_info,
1529 &task_absolutetime_info_len);
1530 if (status != KERN_SUCCESS) {
1531 ERROR("task_info failed: %s", mach_error_string(status));
1532 continue; /* with next thread_list */
1533 }
1535 pse.num_proc++;
1536 pse.vmem_size = task_basic_info.virtual_size;
1537 pse.vmem_rss = task_basic_info.resident_size;
1538 /* Does not seem to be easily exposed */
1539 pse.vmem_data = 0;
1540 pse.vmem_code = 0;
1542 pse.vmem_minflt_counter = task_events_info.cow_faults;
1543 pse.vmem_majflt_counter = task_events_info.faults;
1545 pse.cpu_user_counter = task_absolutetime_info.total_user;
1546 pse.cpu_system_counter = task_absolutetime_info.total_system;
1548 /* context switch counters not implemented */
1549 pse.cswitch_vol = -1;
1550 pse.cswitch_invol = -1;
1551 }
1553 status = task_threads(task_list[task], &thread_list, &thread_list_len);
1554 if (status != KERN_SUCCESS) {
1555 /* Apple's `top' treats this case a zombie. It
1556 * makes sense to some extend: A `zombie'
1557 * thread is nonsense, since the task/process
1558 * is dead. */
1559 zombies++;
1560 DEBUG("task_threads failed: %s", mach_error_string(status));
1561 if (task_list[task] != port_task_self)
1562 mach_port_deallocate(port_task_self, task_list[task]);
1563 continue; /* with next task_list */
1564 }
1566 for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
1567 thread++) {
1568 thread_data_len = THREAD_BASIC_INFO_COUNT;
1569 status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
1570 (thread_info_t)&thread_data, &thread_data_len);
1571 if (status != KERN_SUCCESS) {
1572 ERROR("thread_info failed: %s", mach_error_string(status));
1573 if (task_list[task] != port_task_self)
1574 mach_port_deallocate(port_task_self, thread_list[thread]);
1575 continue; /* with next thread_list */
1576 }
1578 if (ps != NULL)
1579 pse.num_lwp++;
1581 switch (thread_data.run_state) {
1582 case TH_STATE_RUNNING:
1583 running++;
1584 break;
1585 case TH_STATE_STOPPED:
1586 /* What exactly is `halted'? */
1587 case TH_STATE_HALTED:
1588 stopped++;
1589 break;
1590 case TH_STATE_WAITING:
1591 sleeping++;
1592 break;
1593 case TH_STATE_UNINTERRUPTIBLE:
1594 blocked++;
1595 break;
1596 /* There is no `zombie' case here,
1597 * since there are no zombie-threads.
1598 * There's only zombie tasks, which are
1599 * handled above. */
1600 default:
1601 WARNING("Unknown thread status: %i", thread_data.run_state);
1602 break;
1603 } /* switch (thread_data.run_state) */
1605 if (task_list[task] != port_task_self) {
1606 status = mach_port_deallocate(port_task_self, thread_list[thread]);
1607 if (status != KERN_SUCCESS)
1608 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1609 }
1610 } /* for (thread_list) */
1612 if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
1613 thread_list_len * sizeof(thread_act_t))) !=
1614 KERN_SUCCESS) {
1615 ERROR("vm_deallocate failed: %s", mach_error_string(status));
1616 }
1617 thread_list = NULL;
1618 thread_list_len = 0;
1620 /* Only deallocate the task port, if it isn't our own.
1621 * Don't know what would happen in that case, but this
1622 * is what Apple's top does.. ;) */
1623 if (task_list[task] != port_task_self) {
1624 status = mach_port_deallocate(port_task_self, task_list[task]);
1625 if (status != KERN_SUCCESS)
1626 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1627 }
1629 if (ps != NULL)
1630 /* FIXME: cmdline should be here instead of NULL */
1631 ps_list_add(task_name, NULL, &pse);
1632 } /* for (task_list) */
1634 if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
1635 task_list_len * sizeof(task_t))) !=
1636 KERN_SUCCESS) {
1637 ERROR("vm_deallocate failed: %s", mach_error_string(status));
1638 }
1639 task_list = NULL;
1640 task_list_len = 0;
1642 if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
1643 KERN_SUCCESS) {
1644 ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1645 }
1646 } /* for (pset_list) */
1648 ps_submit_state("running", running);
1649 ps_submit_state("sleeping", sleeping);
1650 ps_submit_state("zombies", zombies);
1651 ps_submit_state("stopped", stopped);
1652 ps_submit_state("blocked", blocked);
1654 for (ps = list_head_g; ps != NULL; ps = ps->next)
1655 ps_submit_proc_list(ps);
1656 /* #endif HAVE_THREAD_INFO */
1658 #elif KERNEL_LINUX
1659 int running = 0;
1660 int sleeping = 0;
1661 int zombies = 0;
1662 int stopped = 0;
1663 int paging = 0;
1664 int blocked = 0;
1666 struct dirent *ent;
1667 DIR *proc;
1668 long pid;
1670 char cmdline[CMDLINE_BUFFER_SIZE];
1672 int status;
1673 procstat_t ps;
1674 procstat_entry_t pse;
1675 char state;
1677 running = sleeping = zombies = stopped = paging = blocked = 0;
1678 ps_list_reset();
1680 if ((proc = opendir("/proc")) == NULL) {
1681 char errbuf[1024];
1682 ERROR("Cannot open `/proc': %s", sstrerror(errno, errbuf, sizeof(errbuf)));
1683 return (-1);
1684 }
1686 while ((ent = readdir(proc)) != NULL) {
1687 if (!isdigit(ent->d_name[0]))
1688 continue;
1690 if ((pid = atol(ent->d_name)) < 1)
1691 continue;
1693 status = ps_read_process(pid, &ps, &state);
1694 if (status != 0) {
1695 DEBUG("ps_read_process failed: %i", status);
1696 continue;
1697 }
1699 memset(&pse, 0, sizeof(pse));
1700 pse.id = pid;
1701 pse.age = 0;
1703 pse.num_proc = ps.num_proc;
1704 pse.num_lwp = ps.num_lwp;
1705 pse.vmem_size = ps.vmem_size;
1706 pse.vmem_rss = ps.vmem_rss;
1707 pse.vmem_data = ps.vmem_data;
1708 pse.vmem_code = ps.vmem_code;
1709 pse.stack_size = ps.stack_size;
1711 pse.vmem_minflt = 0;
1712 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1713 pse.vmem_majflt = 0;
1714 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1716 pse.cpu_user = 0;
1717 pse.cpu_user_counter = ps.cpu_user_counter;
1718 pse.cpu_system = 0;
1719 pse.cpu_system_counter = ps.cpu_system_counter;
1721 pse.io_rchar = ps.io_rchar;
1722 pse.io_wchar = ps.io_wchar;
1723 pse.io_syscr = ps.io_syscr;
1724 pse.io_syscw = ps.io_syscw;
1726 pse.cswitch_vol = ps.cswitch_vol;
1727 pse.cswitch_invol = ps.cswitch_invol;
1729 switch (state) {
1730 case 'R':
1731 running++;
1732 break;
1733 case 'S':
1734 sleeping++;
1735 break;
1736 case 'D':
1737 blocked++;
1738 break;
1739 case 'Z':
1740 zombies++;
1741 break;
1742 case 'T':
1743 stopped++;
1744 break;
1745 case 'W':
1746 paging++;
1747 break;
1748 }
1750 ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
1751 &pse);
1752 }
1754 closedir(proc);
1756 ps_submit_state("running", running);
1757 ps_submit_state("sleeping", sleeping);
1758 ps_submit_state("zombies", zombies);
1759 ps_submit_state("stopped", stopped);
1760 ps_submit_state("paging", paging);
1761 ps_submit_state("blocked", blocked);
1763 for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1764 ps_submit_proc_list(ps_ptr);
1766 read_fork_rate();
1767 /* #endif KERNEL_LINUX */
1769 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1770 int running = 0;
1771 int sleeping = 0;
1772 int zombies = 0;
1773 int stopped = 0;
1774 int blocked = 0;
1775 int idle = 0;
1776 int wait = 0;
1778 kvm_t *kd;
1779 char errbuf[_POSIX2_LINE_MAX];
1780 struct kinfo_proc *procs; /* array of processes */
1781 struct kinfo_proc *proc_ptr = NULL;
1782 int count; /* returns number of processes */
1784 procstat_entry_t pse;
1786 ps_list_reset();
1788 /* Open the kvm interface, get a descriptor */
1789 kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
1790 if (kd == NULL) {
1791 ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1792 return (0);
1793 }
1795 /* Get the list of processes. */
1796 procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1797 if (procs == NULL) {
1798 ERROR("processes plugin: Cannot get kvm processes list: %s",
1799 kvm_geterr(kd));
1800 kvm_close(kd);
1801 return (0);
1802 }
1804 /* Iterate through the processes in kinfo_proc */
1805 for (int i = 0; i < count; i++) {
1806 /* Create only one process list entry per _process_, i.e.
1807 * filter out threads (duplicate PID entries). */
1808 if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
1809 char cmdline[CMDLINE_BUFFER_SIZE] = "";
1810 _Bool have_cmdline = 0;
1812 proc_ptr = &(procs[i]);
1813 /* Don't probe system processes and processes without arguments */
1814 if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
1815 char **argv;
1816 int argc;
1817 int status;
1819 /* retrieve the arguments */
1820 argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
1821 argc = 0;
1822 if ((argv != NULL) && (argv[0] != NULL)) {
1823 while (argv[argc] != NULL)
1824 argc++;
1826 status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
1827 if (status < 0)
1828 WARNING("processes plugin: Command line did not fit into buffer.");
1829 else
1830 have_cmdline = 1;
1831 }
1832 } /* if (process has argument list) */
1834 pse.id = procs[i].ki_pid;
1835 pse.age = 0;
1837 pse.num_proc = 1;
1838 pse.num_lwp = procs[i].ki_numthreads;
1840 pse.vmem_size = procs[i].ki_size;
1841 pse.vmem_rss = procs[i].ki_rssize * pagesize;
1842 pse.vmem_data = procs[i].ki_dsize * pagesize;
1843 pse.vmem_code = procs[i].ki_tsize * pagesize;
1844 pse.stack_size = procs[i].ki_ssize * pagesize;
1845 pse.vmem_minflt = 0;
1846 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1847 pse.vmem_majflt = 0;
1848 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1850 pse.cpu_user = 0;
1851 pse.cpu_system = 0;
1852 pse.cpu_user_counter = 0;
1853 pse.cpu_system_counter = 0;
1854 /*
1855 * The u-area might be swapped out, and we can't get
1856 * at it because we have a crashdump and no swap.
1857 * If it's here fill in these fields, otherwise, just
1858 * leave them 0.
1859 */
1860 if (procs[i].ki_flag & P_INMEM) {
1861 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
1862 (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
1863 pse.cpu_system_counter =
1864 procs[i].ki_rusage.ru_stime.tv_usec +
1865 (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
1866 }
1868 /* no I/O data */
1869 pse.io_rchar = -1;
1870 pse.io_wchar = -1;
1871 pse.io_syscr = -1;
1872 pse.io_syscw = -1;
1874 /* context switch counters not implemented */
1875 pse.cswitch_vol = -1;
1876 pse.cswitch_invol = -1;
1878 ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
1880 switch (procs[i].ki_stat) {
1881 case SSTOP:
1882 stopped++;
1883 break;
1884 case SSLEEP:
1885 sleeping++;
1886 break;
1887 case SRUN:
1888 running++;
1889 break;
1890 case SIDL:
1891 idle++;
1892 break;
1893 case SWAIT:
1894 wait++;
1895 break;
1896 case SLOCK:
1897 blocked++;
1898 break;
1899 case SZOMB:
1900 zombies++;
1901 break;
1902 }
1903 } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
1904 }
1906 kvm_close(kd);
1908 ps_submit_state("running", running);
1909 ps_submit_state("sleeping", sleeping);
1910 ps_submit_state("zombies", zombies);
1911 ps_submit_state("stopped", stopped);
1912 ps_submit_state("blocked", blocked);
1913 ps_submit_state("idle", idle);
1914 ps_submit_state("wait", wait);
1916 for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1917 ps_submit_proc_list(ps_ptr);
1918 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1920 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
1921 int running = 0;
1922 int sleeping = 0;
1923 int zombies = 0;
1924 int stopped = 0;
1925 int onproc = 0;
1926 int idle = 0;
1927 int dead = 0;
1929 kvm_t *kd;
1930 char errbuf[1024];
1931 struct kinfo_proc *procs; /* array of processes */
1932 struct kinfo_proc *proc_ptr = NULL;
1933 int count; /* returns number of processes */
1935 procstat_entry_t pse;
1937 ps_list_reset();
1939 /* Open the kvm interface, get a descriptor */
1940 kd = kvm_open(NULL, NULL, NULL, 0, errbuf);
1941 if (kd == NULL) {
1942 ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1943 return (0);
1944 }
1946 /* Get the list of processes. */
1947 procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
1948 if (procs == NULL) {
1949 ERROR("processes plugin: Cannot get kvm processes list: %s",
1950 kvm_geterr(kd));
1951 kvm_close(kd);
1952 return (0);
1953 }
1955 /* Iterate through the processes in kinfo_proc */
1956 for (int i = 0; i < count; i++) {
1957 /* Create only one process list entry per _process_, i.e.
1958 * filter out threads (duplicate PID entries). */
1959 if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
1960 char cmdline[CMDLINE_BUFFER_SIZE] = "";
1961 _Bool have_cmdline = 0;
1963 proc_ptr = &(procs[i]);
1964 /* Don't probe zombie processes */
1965 if (!P_ZOMBIE(proc_ptr)) {
1966 char **argv;
1967 int argc;
1968 int status;
1970 /* retrieve the arguments */
1971 argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
1972 argc = 0;
1973 if ((argv != NULL) && (argv[0] != NULL)) {
1974 while (argv[argc] != NULL)
1975 argc++;
1977 status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
1978 if (status < 0)
1979 WARNING("processes plugin: Command line did not fit into buffer.");
1980 else
1981 have_cmdline = 1;
1982 }
1983 } /* if (process has argument list) */
1985 memset(&pse, 0, sizeof(pse));
1986 pse.id = procs[i].p_pid;
1987 pse.age = 0;
1989 pse.num_proc = 1;
1990 pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
1992 pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
1993 pse.vmem_data = procs[i].p_vm_dsize * pagesize;
1994 pse.vmem_code = procs[i].p_vm_tsize * pagesize;
1995 pse.stack_size = procs[i].p_vm_ssize * pagesize;
1996 pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
1997 pse.vmem_minflt = 0;
1998 pse.vmem_minflt_counter = procs[i].p_uru_minflt;
1999 pse.vmem_majflt = 0;
2000 pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2002 pse.cpu_user = 0;
2003 pse.cpu_system = 0;
2004 pse.cpu_user_counter =
2005 procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2006 pse.cpu_system_counter =
2007 procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2009 /* no I/O data */
2010 pse.io_rchar = -1;
2011 pse.io_wchar = -1;
2012 pse.io_syscr = -1;
2013 pse.io_syscw = -1;
2015 /* context switch counters not implemented */
2016 pse.cswitch_vol = -1;
2017 pse.cswitch_invol = -1;
2019 ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2021 switch (procs[i].p_stat) {
2022 case SSTOP:
2023 stopped++;
2024 break;
2025 case SSLEEP:
2026 sleeping++;
2027 break;
2028 case SRUN:
2029 running++;
2030 break;
2031 case SIDL:
2032 idle++;
2033 break;
2034 case SONPROC:
2035 onproc++;
2036 break;
2037 case SDEAD:
2038 dead++;
2039 break;
2040 case SZOMB:
2041 zombies++;
2042 break;
2043 }
2044 } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
2045 }
2047 kvm_close(kd);
2049 ps_submit_state("running", running);
2050 ps_submit_state("sleeping", sleeping);
2051 ps_submit_state("zombies", zombies);
2052 ps_submit_state("stopped", stopped);
2053 ps_submit_state("onproc", onproc);
2054 ps_submit_state("idle", idle);
2055 ps_submit_state("dead", dead);
2057 for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2058 ps_submit_proc_list(ps_ptr);
2059 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
2061 #elif HAVE_PROCINFO_H
2062 /* AIX */
2063 int running = 0;
2064 int sleeping = 0;
2065 int zombies = 0;
2066 int stopped = 0;
2067 int paging = 0;
2068 int blocked = 0;
2070 pid_t pindex = 0;
2071 int nprocs;
2073 procstat_entry_t pse;
2075 ps_list_reset();
2076 while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
2077 /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
2078 &pindex, MAXPROCENTRY)) > 0) {
2079 for (int i = 0; i < nprocs; i++) {
2080 tid64_t thindex;
2081 int nthreads;
2082 char arglist[MAXARGLN + 1];
2083 char *cargs;
2084 char *cmdline;
2086 if (procentry[i].pi_state == SNONE)
2087 continue;
2088 /* if (procentry[i].pi_state == SZOMB) FIXME */
2090 cmdline = procentry[i].pi_comm;
2091 cargs = procentry[i].pi_comm;
2092 if (procentry[i].pi_flags & SKPROC) {
2093 if (procentry[i].pi_pid == 0)
2094 cmdline = "swapper";
2095 cargs = cmdline;
2096 } else {
2097 if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
2098 MAXARGLN) >= 0) {
2099 int n;
2101 n = -1;
2102 while (++n < MAXARGLN) {
2103 if (arglist[n] == '\0') {
2104 if (arglist[n + 1] == '\0')
2105 break;
2106 arglist[n] = ' ';
2107 }
2108 }
2109 cargs = arglist;
2110 }
2111 }
2113 pse.id = procentry[i].pi_pid;
2114 pse.age = 0;
2115 pse.num_lwp = procentry[i].pi_thcount;
2116 pse.num_proc = 1;
2118 thindex = 0;
2119 while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
2120 sizeof(struct thrdentry64), &thindex,
2121 MAXTHRDENTRY)) > 0) {
2122 int j;
2124 for (j = 0; j < nthreads; j++) {
2125 switch (thrdentry[j].ti_state) {
2126 /* case TSNONE: break; */
2127 case TSIDL:
2128 blocked++;
2129 break; /* FIXME is really blocked */
2130 case TSRUN:
2131 running++;
2132 break;
2133 case TSSLEEP:
2134 sleeping++;
2135 break;
2136 case TSSWAP:
2137 paging++;
2138 break;
2139 case TSSTOP:
2140 stopped++;
2141 break;
2142 case TSZOMB:
2143 zombies++;
2144 break;
2145 }
2146 }
2147 if (nthreads < MAXTHRDENTRY)
2148 break;
2149 }
2151 pse.cpu_user = 0;
2152 /* tv_usec is nanosec ??? */
2153 pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
2154 procentry[i].pi_ru.ru_utime.tv_usec / 1000;
2156 pse.cpu_system = 0;
2157 /* tv_usec is nanosec ??? */
2158 pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
2159 procentry[i].pi_ru.ru_stime.tv_usec / 1000;
2161 pse.vmem_minflt = 0;
2162 pse.vmem_minflt_counter = procentry[i].pi_minflt;
2163 pse.vmem_majflt = 0;
2164 pse.vmem_majflt_counter = procentry[i].pi_majflt;
2166 pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
2167 pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
2168 /* Not supported */
2169 pse.vmem_data = 0;
2170 pse.vmem_code = 0;
2171 pse.stack_size = 0;
2173 pse.io_rchar = -1;
2174 pse.io_wchar = -1;
2175 pse.io_syscr = -1;
2176 pse.io_syscw = -1;
2178 pse.cswitch_vol = -1;
2179 pse.cswitch_invol = -1;
2181 ps_list_add(cmdline, cargs, &pse);
2182 } /* for (i = 0 .. nprocs) */
2184 if (nprocs < MAXPROCENTRY)
2185 break;
2186 } /* while (getprocs64() > 0) */
2187 ps_submit_state("running", running);
2188 ps_submit_state("sleeping", sleeping);
2189 ps_submit_state("zombies", zombies);
2190 ps_submit_state("stopped", stopped);
2191 ps_submit_state("paging", paging);
2192 ps_submit_state("blocked", blocked);
2194 for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
2195 ps_submit_proc_list(ps);
2196 /* #endif HAVE_PROCINFO_H */
2198 #elif KERNEL_SOLARIS
2199 /*
2200 * The Solaris section adds a few more process states and removes some
2201 * process states compared to linux. Most notably there is no "PAGING"
2202 * and "BLOCKED" state for a process. The rest is similar to the linux
2203 * code.
2204 */
2205 int running = 0;
2206 int sleeping = 0;
2207 int zombies = 0;
2208 int stopped = 0;
2209 int detached = 0;
2210 int daemon = 0;
2211 int system = 0;
2212 int orphan = 0;
2214 struct dirent *ent;
2215 DIR *proc;
2217 int status;
2218 char state;
2220 char cmdline[PRARGSZ];
2222 ps_list_reset();
2224 proc = opendir("/proc");
2225 if (proc == NULL)
2226 return (-1);
2228 while ((ent = readdir(proc)) != NULL) {
2229 long pid;
2230 struct procstat ps;
2231 procstat_entry_t pse;
2232 char *endptr;
2234 if (!isdigit((int)ent->d_name[0]))
2235 continue;
2237 pid = strtol(ent->d_name, &endptr, 10);
2238 if (*endptr != 0) /* value didn't completely parse as a number */
2239 continue;
2241 status = ps_read_process(pid, &ps, &state);
2242 if (status != 0) {
2243 DEBUG("ps_read_process failed: %i", status);
2244 continue;
2245 }
2247 memset(&pse, 0, sizeof(pse));
2248 pse.id = pid;
2249 pse.age = 0;
2251 pse.num_proc = ps.num_proc;
2252 pse.num_lwp = ps.num_lwp;
2253 pse.vmem_size = ps.vmem_size;
2254 pse.vmem_rss = ps.vmem_rss;
2255 pse.vmem_data = ps.vmem_data;
2256 pse.vmem_code = ps.vmem_code;
2257 pse.stack_size = ps.stack_size;
2259 pse.vmem_minflt = 0;
2260 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
2261 pse.vmem_majflt = 0;
2262 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
2264 pse.cpu_user = 0;
2265 pse.cpu_user_counter = ps.cpu_user_counter;
2266 pse.cpu_system = 0;
2267 pse.cpu_system_counter = ps.cpu_system_counter;
2269 pse.io_rchar = ps.io_rchar;
2270 pse.io_wchar = ps.io_wchar;
2271 pse.io_syscr = ps.io_syscr;
2272 pse.io_syscw = ps.io_syscw;
2274 pse.cswitch_vol = -1;
2275 pse.cswitch_invol = -1;
2277 switch (state) {
2278 case 'R':
2279 running++;
2280 break;
2281 case 'S':
2282 sleeping++;
2283 break;
2284 case 'E':
2285 detached++;
2286 break;
2287 case 'Z':
2288 zombies++;
2289 break;
2290 case 'T':
2291 stopped++;
2292 break;
2293 case 'A':
2294 daemon++;
2295 break;
2296 case 'Y':
2297 system++;
2298 break;
2299 case 'O':
2300 orphan++;
2301 break;
2302 }
2304 ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
2305 &pse);
2306 } /* while(readdir) */
2307 closedir(proc);
2309 ps_submit_state("running", running);
2310 ps_submit_state("sleeping", sleeping);
2311 ps_submit_state("zombies", zombies);
2312 ps_submit_state("stopped", stopped);
2313 ps_submit_state("detached", detached);
2314 ps_submit_state("daemon", daemon);
2315 ps_submit_state("system", system);
2316 ps_submit_state("orphan", orphan);
2318 for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2319 ps_submit_proc_list(ps_ptr);
2321 read_fork_rate();
2322 #endif /* KERNEL_SOLARIS */
2324 return (0);
2325 } /* int ps_read */
2327 void module_register(void) {
2328 plugin_register_complex_config("processes", ps_config);
2329 plugin_register_init("processes", ps_init);
2330 plugin_register_read("processes", ps_read);
2331 } /* void module_register */