Merge branch 'collectd-4.4' into collectd-4.5
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005  Lyonel Vincent
4  * Copyright (C) 2006-2008  Florian Forster (Mach code)
5  * Copyright (C) 2008  Oleg King
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; either version 2 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Lyonel Vincent <lyonel at ezix.org>
23  *   Florian octo Forster <octo at verplant.org>
24  *   Oleg King <king2 at kaluga.ru>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
30 #include "configfile.h"
31
32 /* Include header files for the mach system, if they exist.. */
33 #if HAVE_THREAD_INFO
34 #  if HAVE_MACH_MACH_INIT_H
35 #    include <mach/mach_init.h>
36 #  endif
37 #  if HAVE_MACH_HOST_PRIV_H
38 #    include <mach/host_priv.h>
39 #  endif
40 #  if HAVE_MACH_MACH_ERROR_H
41 #    include <mach/mach_error.h>
42 #  endif
43 #  if HAVE_MACH_MACH_HOST_H
44 #    include <mach/mach_host.h>
45 #  endif
46 #  if HAVE_MACH_MACH_PORT_H
47 #    include <mach/mach_port.h>
48 #  endif
49 #  if HAVE_MACH_MACH_TYPES_H
50 #    include <mach/mach_types.h>
51 #  endif
52 #  if HAVE_MACH_MESSAGE_H
53 #    include <mach/message.h>
54 #  endif
55 #  if HAVE_MACH_PROCESSOR_SET_H
56 #    include <mach/processor_set.h>
57 #  endif
58 #  if HAVE_MACH_TASK_H
59 #    include <mach/task.h>
60 #  endif
61 #  if HAVE_MACH_THREAD_ACT_H
62 #    include <mach/thread_act.h>
63 #  endif
64 #  if HAVE_MACH_VM_REGION_H
65 #    include <mach/vm_region.h>
66 #  endif
67 #  if HAVE_MACH_VM_MAP_H
68 #    include <mach/vm_map.h>
69 #  endif
70 #  if HAVE_MACH_VM_PROT_H
71 #    include <mach/vm_prot.h>
72 #  endif
73 #  if HAVE_SYS_SYSCTL_H
74 #    include <sys/sysctl.h>
75 #  endif
76 /* #endif HAVE_THREAD_INFO */
77
78 #elif KERNEL_LINUX
79 #  if HAVE_LINUX_CONFIG_H
80 #    include <linux/config.h>
81 #  endif
82 #  ifndef CONFIG_HZ
83 #    define CONFIG_HZ 100
84 #  endif
85 /* #endif KERNEL_LINUX */
86
87 #elif HAVE_LIBKVM_GETPROCS
88 #  include <kvm.h>
89 #  include <sys/user.h>
90 #  include <sys/proc.h>
91 #  if HAVE_SYS_SYSCTL_H
92 #    include <sys/sysctl.h>
93 #  endif
94 /* #endif HAVE_LIBKVM_GETPROCS */
95
96 #else
97 # error "No applicable input method."
98 #endif
99
100 #if HAVE_REGEX_H
101 # include <regex.h>
102 #endif
103
104 #define BUFSIZE 256
105
106 static const char *config_keys[] =
107 {
108         "Process",
109         "ProcessMatch"
110 };
111 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
112
113 typedef struct procstat_entry_s
114 {
115         unsigned long id;
116         unsigned long age;
117
118         unsigned long num_proc;
119         unsigned long num_lwp;
120         unsigned long vmem_rss;
121
122         unsigned long vmem_minflt;
123         unsigned long vmem_majflt;
124         unsigned long vmem_minflt_counter;
125         unsigned long vmem_majflt_counter;
126
127         unsigned long cpu_user;
128         unsigned long cpu_system;
129         unsigned long cpu_user_counter;
130         unsigned long cpu_system_counter;
131
132         struct procstat_entry_s *next;
133 } procstat_entry_t;
134
135 #define PROCSTAT_NAME_LEN 256
136 typedef struct procstat
137 {
138         char          name[PROCSTAT_NAME_LEN];
139 #if HAVE_REGEX_H
140         regex_t *re;
141 #endif
142
143         unsigned long num_proc;
144         unsigned long num_lwp;
145         unsigned long vmem_rss;
146
147         unsigned long vmem_minflt_counter;
148         unsigned long vmem_majflt_counter;
149
150         unsigned long cpu_user_counter;
151         unsigned long cpu_system_counter;
152
153         struct procstat   *next;
154         struct procstat_entry_s *instances;
155 } procstat_t;
156
157 static procstat_t *list_head_g = NULL;
158
159 #if HAVE_THREAD_INFO
160 static mach_port_t port_host_self;
161 static mach_port_t port_task_self;
162
163 static processor_set_name_array_t pset_list;
164 static mach_msg_type_number_t     pset_list_len;
165 /* #endif HAVE_THREAD_INFO */
166
167 #elif KERNEL_LINUX
168 static long pagesize_g;
169 /* #endif KERNEL_LINUX */
170
171 #elif HAVE_LIBKVM_GETPROCS
172 /* no global variables */
173 #endif /* HAVE_LIBKVM_GETPROCS */
174
175 /* put name of process from config to list_head_g tree
176    list_head_g is a list of 'procstat_t' structs with
177    processes names we want to watch */
178 static void ps_list_register (const char *name, const char *regexp)
179 {
180         procstat_t *new;
181         procstat_t *ptr;
182         int status;
183
184         new = (procstat_t *) malloc (sizeof (procstat_t));
185         if (new == NULL)
186         {
187                 ERROR ("processes plugin: ps_list_register: malloc failed.");
188                 return;
189         }
190         memset (new, 0, sizeof (procstat_t));
191         sstrncpy (new->name, name, sizeof (new->name));
192
193 #if HAVE_REGEX_H
194         if (regexp != NULL)
195         {
196                 DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
197                 new->re = (regex_t *) malloc (sizeof (regex_t));
198                 if (new->re == NULL)
199                 {
200                         ERROR ("processes plugin: ps_list_register: malloc failed.");
201                         sfree (new);
202                         return;
203                 }
204
205                 status = regcomp (new->re, regexp, REG_EXTENDED | REG_NOSUB);
206                 if (status != 0)
207                 {
208                         DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
209                         sfree(new->re);
210                         return;
211                 }
212         }
213 #else
214         if (regexp != NULL)
215         {
216                 ERROR ("processes plugin: ps_list_register: "
217                                 "Regular expression \"%s\" found in config "
218                                 "file, but support for regular expressions "
219                                 "has been dispabled at compile time.",
220                                 regexp);
221                 sfree (new);
222                 return;
223         }
224 #endif
225         
226         for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
227         {
228                 if (strcmp (ptr->name, name) == 0)
229                 {
230                         WARNING ("processes plugin: You have configured more "
231                                         "than one `Process' or "
232                                         "`ProcessMatch' with the same name. "
233                                         "All but the first setting will be "
234                                         "ignored.");
235                         sfree (new->re);
236                         sfree (new);
237                         return;
238                 }
239
240                 if (ptr->next == NULL)
241                         break;
242         }
243
244         if (ptr == NULL)
245                 list_head_g = new;
246         else
247                 ptr->next = new;
248 } /* void ps_list_register */
249
250 /* try to match name against entry, returns 1 if success */
251 static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
252 {
253 #if HAVE_REGEX_H
254         if (ps->re != NULL)
255         {
256                 int status;
257                 const char *str;
258
259                 str = cmdline;
260                 if ((str == NULL) || (str[0] == 0))
261                         str = name;
262
263                 assert (str != NULL);
264
265                 status = regexec (ps->re, str,
266                                 /* nmatch = */ 0,
267                                 /* pmatch = */ NULL,
268                                 /* eflags = */ 0);
269                 if (status == 0)
270                         return (1);
271         }
272         else
273 #endif
274         if (strcmp (ps->name, name) == 0)
275                 return (1);
276
277         return (0);
278 } /* int ps_list_match */
279
280 /* add process entry to 'instances' of process 'name' (or refresh it) */
281 static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
282 {
283         procstat_t *ps;
284         procstat_entry_t *pse;
285
286         if (entry->id == 0)
287                 return;
288
289         for (ps = list_head_g; ps != NULL; ps = ps->next)
290         {
291                 if ((ps_list_match (name, cmdline, ps)) == 0)
292                         continue;
293
294                 for (pse = ps->instances; pse != NULL; pse = pse->next)
295                         if ((pse->id == entry->id) || (pse->next == NULL))
296                                 break;
297
298                 if ((pse == NULL) || (pse->id != entry->id))
299                 {
300                         procstat_entry_t *new;
301                         
302                         new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
303                         if (new == NULL)
304                                 return;
305                         memset (new, 0, sizeof (procstat_entry_t));
306                         new->id = entry->id;
307                         
308                         if (pse == NULL)
309                                 ps->instances = new;
310                         else
311                                 pse->next = new;
312
313                         pse = new;
314                 }
315
316                 pse->age = 0;
317                 pse->num_proc = entry->num_proc;
318                 pse->num_lwp  = entry->num_lwp;
319                 pse->vmem_rss = entry->vmem_rss;
320
321                 ps->num_proc += pse->num_proc;
322                 ps->num_lwp  += pse->num_lwp;
323                 ps->vmem_rss += pse->vmem_rss;
324
325                 if ((entry->vmem_minflt_counter == 0)
326                                 && (entry->vmem_majflt_counter == 0))
327                 {
328                         pse->vmem_minflt_counter += entry->vmem_minflt;
329                         pse->vmem_minflt = entry->vmem_minflt;
330
331                         pse->vmem_majflt_counter += entry->vmem_majflt;
332                         pse->vmem_majflt = entry->vmem_majflt;
333                 }
334                 else
335                 {
336                         if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
337                         {
338                                 pse->vmem_minflt = entry->vmem_minflt_counter
339                                         + (ULONG_MAX - pse->vmem_minflt_counter);
340                         }
341                         else
342                         {
343                                 pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
344                         }
345                         pse->vmem_minflt_counter = entry->vmem_minflt_counter;
346                         
347                         if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
348                         {
349                                 pse->vmem_majflt = entry->vmem_majflt_counter
350                                         + (ULONG_MAX - pse->vmem_majflt_counter);
351                         }
352                         else
353                         {
354                                 pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
355                         }
356                         pse->vmem_majflt_counter = entry->vmem_majflt_counter;
357                 }
358
359                 ps->vmem_minflt_counter += pse->vmem_minflt;
360                 ps->vmem_majflt_counter += pse->vmem_majflt;
361
362                 if ((entry->cpu_user_counter == 0)
363                                 && (entry->cpu_system_counter == 0))
364                 {
365                         pse->cpu_user_counter += entry->cpu_user;
366                         pse->cpu_user = entry->cpu_user;
367
368                         pse->cpu_system_counter += entry->cpu_system;
369                         pse->cpu_system = entry->cpu_system;
370                 }
371                 else
372                 {
373                         if (entry->cpu_user_counter < pse->cpu_user_counter)
374                         {
375                                 pse->cpu_user = entry->cpu_user_counter
376                                         + (ULONG_MAX - pse->cpu_user_counter);
377                         }
378                         else
379                         {
380                                 pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
381                         }
382                         pse->cpu_user_counter = entry->cpu_user_counter;
383                         
384                         if (entry->cpu_system_counter < pse->cpu_system_counter)
385                         {
386                                 pse->cpu_system = entry->cpu_system_counter
387                                         + (ULONG_MAX - pse->cpu_system_counter);
388                         }
389                         else
390                         {
391                                 pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
392                         }
393                         pse->cpu_system_counter = entry->cpu_system_counter;
394                 }
395
396                 ps->cpu_user_counter   += pse->cpu_user;
397                 ps->cpu_system_counter += pse->cpu_system;
398         }
399 }
400
401 /* remove old entries from instances of processes in list_head_g */
402 static void ps_list_reset (void)
403 {
404         procstat_t *ps;
405         procstat_entry_t *pse;
406         procstat_entry_t *pse_prev;
407
408         for (ps = list_head_g; ps != NULL; ps = ps->next)
409         {
410                 ps->num_proc    = 0;
411                 ps->num_lwp     = 0;
412                 ps->vmem_rss    = 0;
413
414                 pse_prev = NULL;
415                 pse = ps->instances;
416                 while (pse != NULL)
417                 {
418                         if (pse->age > 10)
419                         {
420                                 DEBUG ("Removing this procstat entry cause it's too old: "
421                                                 "id = %lu; name = %s;",
422                                                 pse->id, ps->name);
423
424                                 if (pse_prev == NULL)
425                                 {
426                                         ps->instances = pse->next;
427                                         free (pse);
428                                         pse = ps->instances;
429                                 }
430                                 else
431                                 {
432                                         pse_prev->next = pse->next;
433                                         free (pse);
434                                         pse = pse_prev->next;
435                                 }
436                         }
437                         else
438                         {
439                                 pse->age++;
440                                 pse_prev = pse;
441                                 pse = pse->next;
442                         }
443                 } /* while (pse != NULL) */
444         } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
445 }
446
447 /* put all pre-defined 'Process' names from config to list_head_g tree */
448 static int ps_config (const char *key, const char *value)
449 {
450         if (strcasecmp (key, "Process") == 0)
451         {
452                 ps_list_register (value, NULL);
453         }
454         else if (strcasecmp (key, "ProcessMatch") == 0)
455         {
456                 char *new_val;
457                 char *fields[3];
458                 int fields_num;
459
460                 new_val = strdup (value);
461                 if (new_val == NULL)
462                         return (1);
463                 fields_num = strsplit (new_val, fields,
464                                 STATIC_ARRAY_SIZE (fields));
465                 if (fields_num != 2)
466                 {
467                         sfree (new_val);
468                         return (1);
469                 }
470                 ps_list_register (fields[0], fields[1]);
471                 sfree (new_val);
472         }
473         else
474         {
475                 return (-1);
476         }
477
478         return (0);
479 }
480
481 static int ps_init (void)
482 {
483 #if HAVE_THREAD_INFO
484         kern_return_t status;
485
486         port_host_self = mach_host_self ();
487         port_task_self = mach_task_self ();
488
489         if (pset_list != NULL)
490         {
491                 vm_deallocate (port_task_self,
492                                 (vm_address_t) pset_list,
493                                 pset_list_len * sizeof (processor_set_t));
494                 pset_list = NULL;
495                 pset_list_len = 0;
496         }
497
498         if ((status = host_processor_sets (port_host_self,
499                                         &pset_list,
500                                         &pset_list_len)) != KERN_SUCCESS)
501         {
502                 ERROR ("host_processor_sets failed: %s\n",
503                                 mach_error_string (status));
504                 pset_list = NULL;
505                 pset_list_len = 0;
506                 return (-1);
507         }
508 /* #endif HAVE_THREAD_INFO */
509
510 #elif KERNEL_LINUX
511         pagesize_g = sysconf(_SC_PAGESIZE);
512         DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
513                         pagesize_g, CONFIG_HZ);
514 /* #endif KERNEL_LINUX */
515
516 #elif HAVE_LIBKVM_GETPROCS
517 /* no initialization */
518 #endif /* HAVE_LIBKVM_GETPROCS */
519
520         return (0);
521 } /* int ps_init */
522
523 /* submit global state (e.g.: qty of zombies, running, etc..) */
524 static void ps_submit_state (const char *state, double value)
525 {
526         value_t values[1];
527         value_list_t vl = VALUE_LIST_INIT;
528
529         values[0].gauge = value;
530
531         vl.values = values;
532         vl.values_len = 1;
533         vl.time = time (NULL);
534         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
535         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
536         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
537         sstrncpy (vl.type, "ps_state", sizeof (vl.type));
538         sstrncpy (vl.type_instance, state, sizeof (vl.type_instance));
539
540         plugin_dispatch_values (&vl);
541 }
542
543 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
544 static void ps_submit_proc_list (procstat_t *ps)
545 {
546         value_t values[2];
547         value_list_t vl = VALUE_LIST_INIT;
548
549         vl.values = values;
550         vl.values_len = 2;
551         vl.time = time (NULL);
552         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
553         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
554         sstrncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
555
556         sstrncpy (vl.type, "ps_rss", sizeof (vl.type));
557         vl.values[0].gauge = ps->vmem_rss;
558         vl.values_len = 1;
559         plugin_dispatch_values (&vl);
560
561         sstrncpy (vl.type, "ps_cputime", sizeof (vl.type));
562         vl.values[0].counter = ps->cpu_user_counter;
563         vl.values[1].counter = ps->cpu_system_counter;
564         vl.values_len = 2;
565         plugin_dispatch_values (&vl);
566
567         sstrncpy (vl.type, "ps_count", sizeof (vl.type));
568         vl.values[0].gauge = ps->num_proc;
569         vl.values[1].gauge = ps->num_lwp;
570         vl.values_len = 2;
571         plugin_dispatch_values (&vl);
572
573         sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type));
574         vl.values[0].counter = ps->vmem_minflt_counter;
575         vl.values[1].counter = ps->vmem_majflt_counter;
576         vl.values_len = 2;
577         plugin_dispatch_values (&vl);
578
579         DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
580                         "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
581                         "cpu_user_counter = %lu; cpu_system_counter = %lu;",
582                         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
583                         ps->vmem_minflt_counter, ps->vmem_majflt_counter,
584                         ps->cpu_user_counter, ps->cpu_system_counter);
585 } /* void ps_submit_proc_list */
586
587 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
588 #if KERNEL_LINUX
589 static int *ps_read_tasks (int pid)
590 {
591         int *list = NULL;
592         int  list_size = 1; /* size of allocated space, in elements */
593         int  list_len = 0;  /* number of currently used elements */
594
595         char           dirname[64];
596         DIR           *dh;
597         struct dirent *ent;
598
599         ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid);
600
601         if ((dh = opendir (dirname)) == NULL)
602         {
603                 DEBUG ("Failed to open directory `%s'", dirname);
604                 return (NULL);
605         }
606
607         while ((ent = readdir (dh)) != NULL)
608         {
609                 if (!isdigit (ent->d_name[0]))
610                         continue;
611
612                 if ((list_len + 1) >= list_size)
613                 {
614                         int *new_ptr;
615                         int  new_size = 2 * list_size;
616                         /* Comes in sizes: 2, 4, 8, 16, ... */
617
618                         new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
619                         if (new_ptr == NULL)
620                         {
621                                 if (list != NULL)
622                                         free (list);
623                                 ERROR ("processes plugin: "
624                                                 "Failed to allocate more memory.");
625                                 return (NULL);
626                         }
627
628                         list = new_ptr;
629                         list_size = new_size;
630
631                         memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
632                 }
633
634                 list[list_len] = atoi (ent->d_name);
635                 if (list[list_len] != 0)
636                         list_len++;
637         }
638
639         closedir (dh);
640
641         if (list_len == 0)
642                 return (NULL);
643
644         assert (list_len < list_size);
645         assert (list[list_len] == 0);
646
647         return (list);
648 } /* int *ps_read_tasks */
649
650 int ps_read_process (int pid, procstat_t *ps, char *state)
651 {
652         char  filename[64];
653         char  buffer[1024];
654
655         char *fields[64];
656         char  fields_len;
657
658         int  *tasks;
659         int   i;
660
661         int   ppid;
662         int   name_len;
663
664         long long unsigned cpu_user_counter;
665         long long unsigned cpu_system_counter;
666         long long unsigned vmem_rss;
667
668         memset (ps, 0, sizeof (procstat_t));
669
670         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
671
672         i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
673         if (i <= 0)
674                 return (-1);
675         buffer[i] = 0;
676
677         fields_len = strsplit (buffer, fields, 64);
678         if (fields_len < 24)
679         {
680                 DEBUG ("processes plugin: ps_read_process (pid = %i):"
681                                 " `%s' has only %i fields..",
682                                 (int) pid, filename, fields_len);
683                 return (-1);
684         }
685
686         /* copy the name, strip brackets in the process */
687         name_len = strlen (fields[1]) - 2;
688         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
689         {
690                 DEBUG ("No brackets found in process name: `%s'", fields[1]);
691                 return (-1);
692         }
693         fields[1] = fields[1] + 1;
694         fields[1][name_len] = '\0';
695         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
696
697         ppid = atoi (fields[3]);
698
699         *state = fields[2][0];
700
701         if (*state == 'Z')
702         {
703                 ps->num_lwp  = 0;
704                 ps->num_proc = 0;
705         }
706         else if ((tasks = ps_read_tasks (pid)) == NULL)
707         {
708                 /* Kernel 2.4 or so */
709                 ps->num_lwp  = 1;
710                 ps->num_proc = 1;
711         }
712         else
713         {
714                 ps->num_lwp  = 0;
715                 ps->num_proc = 1;
716                 for (i = 0; tasks[i] != 0; i++)
717                         ps->num_lwp++;
718
719                 free (tasks);
720                 tasks = NULL;
721         }
722
723         /* Leave the rest at zero if this is only a zombi */
724         if (ps->num_proc == 0)
725         {
726                 DEBUG ("processes plugin: This is only a zombi: pid = %i; "
727                                 "name = %s;", pid, ps->name);
728                 return (0);
729         }
730
731         cpu_user_counter   = atoll (fields[13]);
732         cpu_system_counter = atoll (fields[14]);
733         vmem_rss = atoll (fields[23]);
734         ps->vmem_minflt_counter = atol (fields[9]);
735         ps->vmem_majflt_counter = atol (fields[11]);
736         
737         /* Convert jiffies to useconds */
738         cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
739         cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
740         vmem_rss = vmem_rss * pagesize_g;
741
742         ps->cpu_user_counter = (unsigned long) cpu_user_counter;
743         ps->cpu_system_counter = (unsigned long) cpu_system_counter;
744         ps->vmem_rss = (unsigned long) vmem_rss;
745
746         /* success */
747         return (0);
748 } /* int ps_read_process (...) */
749 #endif /* KERNEL_LINUX */
750
751 #if HAVE_THREAD_INFO
752 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
753 {
754         int mib[4];
755
756         struct kinfo_proc kp;
757         size_t            kp_size;
758
759         mib[0] = CTL_KERN;
760         mib[1] = KERN_PROC;
761         mib[2] = KERN_PROC_PID;
762
763         if (pid_for_task (t, pid) != KERN_SUCCESS)
764                 return (-1);
765         mib[3] = *pid;
766
767         kp_size = sizeof (kp);
768         if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
769                 return (-1);
770
771         if (name_max_len > (MAXCOMLEN + 1))
772                 name_max_len = MAXCOMLEN + 1;
773
774         strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
775         name[name_max_len - 1] = '\0';
776
777         DEBUG ("pid = %i; name = %s;", *pid, name);
778
779         /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
780          * `top' does it, because it is a lot of work and only used when
781          * debugging. -octo */
782
783         return (0);
784 }
785 #endif /* HAVE_THREAD_INFO */
786 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
787
788 /* do actual readings from kernel */
789 static int ps_read (void)
790 {
791 #if HAVE_THREAD_INFO
792         kern_return_t            status;
793
794         int                      pset;
795         processor_set_t          port_pset_priv;
796
797         int                      task;
798         task_array_t             task_list;
799         mach_msg_type_number_t   task_list_len;
800
801         int                      task_pid;
802         char                     task_name[MAXCOMLEN + 1];
803
804         int                      thread;
805         thread_act_array_t       thread_list;
806         mach_msg_type_number_t   thread_list_len;
807         thread_basic_info_data_t thread_data;
808         mach_msg_type_number_t   thread_data_len;
809
810         int running  = 0;
811         int sleeping = 0;
812         int zombies  = 0;
813         int stopped  = 0;
814         int blocked  = 0;
815
816         procstat_t *ps;
817         procstat_entry_t pse;
818
819         ps_list_reset ();
820
821         /*
822          * The Mach-concept is a little different from the traditional UNIX
823          * concept: All the work is done in threads. Threads are contained in
824          * `tasks'. Therefore, `task status' doesn't make much sense, since
825          * it's actually a `thread status'.
826          * Tasks are assigned to sets of processors, so that's where you go to
827          * get a list.
828          */
829         for (pset = 0; pset < pset_list_len; pset++)
830         {
831                 if ((status = host_processor_set_priv (port_host_self,
832                                                 pset_list[pset],
833                                                 &port_pset_priv)) != KERN_SUCCESS)
834                 {
835                         ERROR ("host_processor_set_priv failed: %s\n",
836                                         mach_error_string (status));
837                         continue;
838                 }
839
840                 if ((status = processor_set_tasks (port_pset_priv,
841                                                 &task_list,
842                                                 &task_list_len)) != KERN_SUCCESS)
843                 {
844                         ERROR ("processor_set_tasks failed: %s\n",
845                                         mach_error_string (status));
846                         mach_port_deallocate (port_task_self, port_pset_priv);
847                         continue;
848                 }
849
850                 for (task = 0; task < task_list_len; task++)
851                 {
852                         ps = NULL;
853                         if (mach_get_task_name (task_list[task],
854                                                 &task_pid,
855                                                 task_name, PROCSTAT_NAME_LEN) == 0)
856                         {
857                                 /* search for at least one match */
858                                 for (ps = list_head_g; ps != NULL; ps = ps->next)
859                                         /* FIXME: cmdline should be here instead of NULL */
860                                         if (ps_list_match (task_name, NULL, ps) == 1)
861                                                 break;
862                         }
863
864                         /* Collect more detailed statistics for this process */
865                         if (ps != NULL)
866                         {
867                                 task_basic_info_data_t        task_basic_info;
868                                 mach_msg_type_number_t        task_basic_info_len;
869                                 task_events_info_data_t       task_events_info;
870                                 mach_msg_type_number_t        task_events_info_len;
871                                 task_absolutetime_info_data_t task_absolutetime_info;
872                                 mach_msg_type_number_t        task_absolutetime_info_len;
873
874                                 memset (&pse, '\0', sizeof (pse));
875                                 pse.id = task_pid;
876
877                                 task_basic_info_len = TASK_BASIC_INFO_COUNT;
878                                 status = task_info (task_list[task],
879                                                 TASK_BASIC_INFO,
880                                                 (task_info_t) &task_basic_info,
881                                                 &task_basic_info_len);
882                                 if (status != KERN_SUCCESS)
883                                 {
884                                         ERROR ("task_info failed: %s",
885                                                         mach_error_string (status));
886                                         continue; /* with next thread_list */
887                                 }
888
889                                 task_events_info_len = TASK_EVENTS_INFO_COUNT;
890                                 status = task_info (task_list[task],
891                                                 TASK_EVENTS_INFO,
892                                                 (task_info_t) &task_events_info,
893                                                 &task_events_info_len);
894                                 if (status != KERN_SUCCESS)
895                                 {
896                                         ERROR ("task_info failed: %s",
897                                                         mach_error_string (status));
898                                         continue; /* with next thread_list */
899                                 }
900
901                                 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
902                                 status = task_info (task_list[task],
903                                                 TASK_ABSOLUTETIME_INFO,
904                                                 (task_info_t) &task_absolutetime_info,
905                                                 &task_absolutetime_info_len);
906                                 if (status != KERN_SUCCESS)
907                                 {
908                                         ERROR ("task_info failed: %s",
909                                                         mach_error_string (status));
910                                         continue; /* with next thread_list */
911                                 }
912
913                                 pse.num_proc++;
914                                 pse.vmem_rss = task_basic_info.resident_size;
915
916                                 pse.vmem_minflt_counter = task_events_info.cow_faults;
917                                 pse.vmem_majflt_counter = task_events_info.faults;
918
919                                 pse.cpu_user_counter = task_absolutetime_info.total_user;
920                                 pse.cpu_system_counter = task_absolutetime_info.total_system;
921                         }
922
923                         status = task_threads (task_list[task], &thread_list,
924                                         &thread_list_len);
925                         if (status != KERN_SUCCESS)
926                         {
927                                 /* Apple's `top' treats this case a zombie. It
928                                  * makes sense to some extend: A `zombie'
929                                  * thread is nonsense, since the task/process
930                                  * is dead. */
931                                 zombies++;
932                                 DEBUG ("task_threads failed: %s",
933                                                 mach_error_string (status));
934                                 if (task_list[task] != port_task_self)
935                                         mach_port_deallocate (port_task_self,
936                                                         task_list[task]);
937                                 continue; /* with next task_list */
938                         }
939
940                         for (thread = 0; thread < thread_list_len; thread++)
941                         {
942                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
943                                 status = thread_info (thread_list[thread],
944                                                 THREAD_BASIC_INFO,
945                                                 (thread_info_t) &thread_data,
946                                                 &thread_data_len);
947                                 if (status != KERN_SUCCESS)
948                                 {
949                                         ERROR ("thread_info failed: %s",
950                                                         mach_error_string (status));
951                                         if (task_list[task] != port_task_self)
952                                                 mach_port_deallocate (port_task_self,
953                                                                 thread_list[thread]);
954                                         continue; /* with next thread_list */
955                                 }
956
957                                 if (ps != NULL)
958                                         pse.num_lwp++;
959
960                                 switch (thread_data.run_state)
961                                 {
962                                         case TH_STATE_RUNNING:
963                                                 running++;
964                                                 break;
965                                         case TH_STATE_STOPPED:
966                                         /* What exactly is `halted'? */
967                                         case TH_STATE_HALTED:
968                                                 stopped++;
969                                                 break;
970                                         case TH_STATE_WAITING:
971                                                 sleeping++;
972                                                 break;
973                                         case TH_STATE_UNINTERRUPTIBLE:
974                                                 blocked++;
975                                                 break;
976                                         /* There is no `zombie' case here,
977                                          * since there are no zombie-threads.
978                                          * There's only zombie tasks, which are
979                                          * handled above. */
980                                         default:
981                                                 WARNING ("Unknown thread status: %i",
982                                                                 thread_data.run_state);
983                                                 break;
984                                 } /* switch (thread_data.run_state) */
985
986                                 if (task_list[task] != port_task_self)
987                                 {
988                                         status = mach_port_deallocate (port_task_self,
989                                                         thread_list[thread]);
990                                         if (status != KERN_SUCCESS)
991                                                 ERROR ("mach_port_deallocate failed: %s",
992                                                                 mach_error_string (status));
993                                 }
994                         } /* for (thread_list) */
995
996                         if ((status = vm_deallocate (port_task_self,
997                                                         (vm_address_t) thread_list,
998                                                         thread_list_len * sizeof (thread_act_t)))
999                                         != KERN_SUCCESS)
1000                         {
1001                                 ERROR ("vm_deallocate failed: %s",
1002                                                 mach_error_string (status));
1003                         }
1004                         thread_list = NULL;
1005                         thread_list_len = 0;
1006
1007                         /* Only deallocate the task port, if it isn't our own.
1008                          * Don't know what would happen in that case, but this
1009                          * is what Apple's top does.. ;) */
1010                         if (task_list[task] != port_task_self)
1011                         {
1012                                 status = mach_port_deallocate (port_task_self,
1013                                                 task_list[task]);
1014                                 if (status != KERN_SUCCESS)
1015                                         ERROR ("mach_port_deallocate failed: %s",
1016                                                         mach_error_string (status));
1017                         }
1018
1019                         if (ps != NULL)
1020                                 /* FIXME: cmdline should be here instead of NULL */
1021                                 ps_list_add (task_name, NULL, &pse);
1022                 } /* for (task_list) */
1023
1024                 if ((status = vm_deallocate (port_task_self,
1025                                 (vm_address_t) task_list,
1026                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
1027                 {
1028                         ERROR ("vm_deallocate failed: %s",
1029                                         mach_error_string (status));
1030                 }
1031                 task_list = NULL;
1032                 task_list_len = 0;
1033
1034                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
1035                                 != KERN_SUCCESS)
1036                 {
1037                         ERROR ("mach_port_deallocate failed: %s",
1038                                         mach_error_string (status));
1039                 }
1040         } /* for (pset_list) */
1041
1042         ps_submit_state ("running", running);
1043         ps_submit_state ("sleeping", sleeping);
1044         ps_submit_state ("zombies", zombies);
1045         ps_submit_state ("stopped", stopped);
1046         ps_submit_state ("blocked", blocked);
1047
1048         for (ps = list_head_g; ps != NULL; ps = ps->next)
1049                 ps_submit_proc_list (ps);
1050 /* #endif HAVE_THREAD_INFO */
1051
1052 #elif KERNEL_LINUX
1053         int running  = 0;
1054         int sleeping = 0;
1055         int zombies  = 0;
1056         int stopped  = 0;
1057         int paging   = 0;
1058         int blocked  = 0;
1059
1060         struct dirent *ent;
1061         DIR           *proc;
1062         int            pid;
1063
1064         int        status;
1065         procstat_t ps;
1066         procstat_entry_t pse;
1067         char       state;
1068
1069         procstat_t *ps_ptr;
1070
1071         running = sleeping = zombies = stopped = paging = blocked = 0;
1072         ps_list_reset ();
1073
1074         if ((proc = opendir ("/proc")) == NULL)
1075         {
1076                 char errbuf[1024];
1077                 ERROR ("Cannot open `/proc': %s",
1078                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1079                 return (-1);
1080         }
1081
1082         while ((ent = readdir (proc)) != NULL)
1083         {
1084                 if (!isdigit (ent->d_name[0]))
1085                         continue;
1086
1087                 if ((pid = atoi (ent->d_name)) < 1)
1088                         continue;
1089
1090                 status = ps_read_process (pid, &ps, &state);
1091                 if (status != 0)
1092                 {
1093                         DEBUG ("ps_read_process failed: %i", status);
1094                         continue;
1095                 }
1096
1097                 pse.id       = pid;
1098                 pse.age      = 0;
1099
1100                 pse.num_proc = ps.num_proc;
1101                 pse.num_lwp  = ps.num_lwp;
1102                 pse.vmem_rss = ps.vmem_rss;
1103
1104                 pse.vmem_minflt = 0;
1105                 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1106                 pse.vmem_majflt = 0;
1107                 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1108
1109                 pse.cpu_user = 0;
1110                 pse.cpu_user_counter = ps.cpu_user_counter;
1111                 pse.cpu_system = 0;
1112                 pse.cpu_system_counter = ps.cpu_system_counter;
1113
1114                 switch (state)
1115                 {
1116                         case 'R': running++;  break;
1117                         case 'S': sleeping++; break;
1118                         case 'D': blocked++;  break;
1119                         case 'Z': zombies++;  break;
1120                         case 'T': stopped++;  break;
1121                         case 'W': paging++;   break;
1122                 }
1123
1124                 /* FIXME: cmdline should be here instead of NULL */
1125                 ps_list_add (ps.name, NULL, &pse);
1126         }
1127
1128         closedir (proc);
1129
1130         ps_submit_state ("running",  running);
1131         ps_submit_state ("sleeping", sleeping);
1132         ps_submit_state ("zombies",  zombies);
1133         ps_submit_state ("stopped",  stopped);
1134         ps_submit_state ("paging",   paging);
1135         ps_submit_state ("blocked",  blocked);
1136
1137         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1138                 ps_submit_proc_list (ps_ptr);
1139 /* #endif KERNEL_LINUX */
1140
1141 #elif HAVE_LIBKVM_GETPROCS
1142         int running  = 0;
1143         int sleeping = 0;
1144         int zombies  = 0;
1145         int stopped  = 0;
1146         int blocked  = 0;
1147         int idle     = 0;
1148         int wait     = 0;
1149
1150         kvm_t *kd;
1151         char errbuf[1024];
1152         char cmdline[ARG_MAX];
1153         char *cmdline_ptr;
1154         struct kinfo_proc *procs;          /* array of processes */
1155         char **argv;
1156         int count;                         /* returns number of processes */
1157         int i;
1158
1159         procstat_t *ps_ptr;
1160         procstat_entry_t pse;
1161
1162         ps_list_reset ();
1163
1164         /* Open the kvm interface, get a descriptor */
1165         kd = kvm_open (NULL, NULL, NULL, 0, errbuf);
1166         if (kd == NULL)
1167         {
1168                 ERROR ("processes plugin: Cannot open kvm interface: %s",
1169                                 errbuf);
1170                 return (0);
1171         }
1172
1173         /* Get the list of processes. */
1174         procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1175         if (procs == NULL)
1176         {
1177                 kvm_close (kd);
1178                 ERROR ("processes plugin: Cannot get kvm processes list: %s",
1179                                 kvm_geterr(kd));
1180                 return (0);
1181         }
1182
1183         /* Iterate through the processes in kinfo_proc */
1184         for (i = 0; i < count; i++)
1185         {
1186                 /* retrieve the arguments */
1187                 cmdline[0] = 0;
1188                 cmdline_ptr = NULL;
1189
1190                 argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
1191                 if (argv != NULL)
1192                 {
1193                         int status;
1194                         int argc;
1195
1196                         argc = 0;
1197                         while (argv[argc] != NULL)
1198                                 argc++;
1199
1200                         status = strjoin (cmdline, sizeof (cmdline),
1201                                         argv, argc, " ");
1202
1203                         if (status < 0)
1204                         {
1205                                 WARNING ("processes plugin: Command line did "
1206                                                 "not fit into buffer.");
1207                         }
1208                         else
1209                         {
1210                                 cmdline_ptr = &cmdline[0];
1211                         }
1212                 }
1213
1214                 pse.id       = procs[i].ki_pid;
1215                 pse.age      = 0;
1216
1217                 pse.num_proc = 1;
1218                 pse.num_lwp  = procs[i].ki_numthreads;
1219
1220                 pse.vmem_rss = procs[i].ki_rssize * getpagesize();
1221                 pse.vmem_minflt = 0;
1222                 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1223                 pse.vmem_majflt = 0;
1224                 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1225
1226                 pse.cpu_user = 0;
1227                 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
1228                         * 1000
1229                         + procs[i].ki_rusage.ru_utime.tv_usec;
1230                 pse.cpu_system = 0;
1231                 pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
1232                         * 1000
1233                         + procs[i].ki_rusage.ru_stime.tv_usec;
1234
1235                 switch (procs[i].ki_stat)
1236                 {
1237                         case SSTOP:     stopped++;      break;
1238                         case SSLEEP:    sleeping++;     break;
1239                         case SRUN:      running++;      break;
1240                         case SIDL:      idle++;         break;
1241                         case SWAIT:     wait++;         break;
1242                         case SLOCK:     blocked++;      break;
1243                         case SZOMB:     zombies++;      break;
1244                 }
1245
1246                 ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
1247         }
1248
1249         kvm_close(kd);
1250
1251         ps_submit_state ("running",  running);
1252         ps_submit_state ("sleeping", sleeping);
1253         ps_submit_state ("zombies",  zombies);
1254         ps_submit_state ("stopped",  stopped);
1255         ps_submit_state ("blocked",  blocked);
1256         ps_submit_state ("idle",     idle);
1257         ps_submit_state ("wait",     wait);
1258
1259         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1260                 ps_submit_proc_list (ps_ptr);
1261 #endif /* HAVE_LIBKVM_GETPROCS */
1262
1263         return (0);
1264 } /* int ps_read */
1265
1266 void module_register (void)
1267 {
1268         plugin_register_config ("processes", ps_config,
1269                         config_keys, config_keys_num);
1270         plugin_register_init ("processes", ps_init);
1271         plugin_register_read ("processes", ps_read);
1272 } /* void module_register */