src/processes.c: Remove an unused variable.
[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_KVM_H
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_KVM_H */
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         NULL
111 };
112 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
113
114 typedef struct procstat_entry_s
115 {
116         unsigned long id;
117         unsigned long age;
118
119         unsigned long num_proc;
120         unsigned long num_lwp;
121         unsigned long vmem_rss;
122
123         unsigned long vmem_minflt;
124         unsigned long vmem_majflt;
125         unsigned long vmem_minflt_counter;
126         unsigned long vmem_majflt_counter;
127
128         unsigned long cpu_user;
129         unsigned long cpu_system;
130         unsigned long cpu_user_counter;
131         unsigned long cpu_system_counter;
132
133         struct procstat_entry_s *next;
134 } procstat_entry_t;
135
136 #define PROCSTAT_NAME_LEN 256
137 typedef struct procstat
138 {
139         char          name[PROCSTAT_NAME_LEN];
140 #if HAVE_REGEX_H
141         regex_t *re;
142 #endif
143
144         unsigned long num_proc;
145         unsigned long num_lwp;
146         unsigned long vmem_rss;
147
148         unsigned long vmem_minflt_counter;
149         unsigned long vmem_majflt_counter;
150
151         unsigned long cpu_user_counter;
152         unsigned long cpu_system_counter;
153
154         struct procstat   *next;
155         struct procstat_entry_s *instances;
156 } procstat_t;
157
158 static procstat_t *list_head_g = NULL;
159
160 #if HAVE_THREAD_INFO
161 static mach_port_t port_host_self;
162 static mach_port_t port_task_self;
163
164 static processor_set_name_array_t pset_list;
165 static mach_msg_type_number_t     pset_list_len;
166 /* #endif HAVE_THREAD_INFO */
167
168 #elif KERNEL_LINUX
169 static long pagesize_g;
170 #endif /* KERNEL_LINUX */
171
172 /* put name of process from config to list_head_g tree
173    list_head_g is a list of 'procstat_t' structs with
174    processes names we want to watch */
175 static void ps_list_register (const char *name, const char *regexp)
176 {
177         procstat_t *new;
178         procstat_t *ptr;
179         int status;
180
181         new = (procstat_t *) malloc (sizeof (procstat_t));
182         if (new == NULL)
183         {
184                 ERROR ("processes plugin: ps_list_register: malloc failed.");
185                 return;
186         }
187         memset (new, 0, sizeof (procstat_t));
188         sstrncpy (new->name, name, sizeof (new->name));
189
190 #if HAVE_REGEX_H
191         if (regexp != NULL)
192         {
193                 DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
194                 new->re = (regex_t *) malloc (sizeof (regex_t));
195                 if (new->re == NULL)
196                 {
197                         ERROR ("processes plugin: ps_list_register: malloc failed.");
198                         sfree (new);
199                         return;
200                 }
201
202                 status = regcomp (new->re, regexp, REG_EXTENDED | REG_NOSUB);
203                 if (status != 0)
204                 {
205                         DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
206                         sfree(new->re);
207                         return;
208                 }
209         }
210 #else
211         if (regexp != NULL)
212         {
213                 ERROR ("processes plugin: ps_list_register: "
214                                 "Regular expression \"%s\" found in config "
215                                 "file, but support for regular expressions "
216                                 "has been dispabled at compile time.",
217                                 regexp);
218                 sfree (new);
219                 return;
220         }
221 #endif
222         
223         for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
224         {
225                 if (strcmp (ptr->name, name) == 0)
226                 {
227                         WARNING ("processes plugin: You have configured more "
228                                         "than one `Process' or "
229                                         "`ProcessMatch' with the same name. "
230                                         "All but the first setting will be "
231                                         "ignored.");
232                         sfree (new->re);
233                         sfree (new);
234                         return;
235                 }
236
237                 if (ptr->next == NULL)
238                         break;
239         }
240
241         if (ptr == NULL)
242                 list_head_g = new;
243         else
244                 ptr->next = new;
245 } /* void ps_list_register */
246
247 /* try to match name against entry, returns 1 if success */
248 static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
249 {
250 #if HAVE_REGEX_H
251         if (ps->re != NULL)
252         {
253                 int status;
254                 const char *str;
255
256                 str = cmdline;
257                 if ((str == NULL) || (str[0] == 0))
258                         str = name;
259
260                 assert (str != NULL);
261
262                 status = regexec (ps->re, str,
263                                 /* nmatch = */ 0,
264                                 /* pmatch = */ NULL,
265                                 /* eflags = */ 0);
266                 if (status == 0)
267                         return (1);
268         }
269         else
270 #endif
271         if (strcmp (ps->name, name) == 0)
272                 return (1);
273
274         return (0);
275 } /* int ps_list_match */
276
277 /* add process entry to 'instances' of process 'name' (or refresh it) */
278 static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
279 {
280         procstat_t *ps;
281         procstat_entry_t *pse;
282
283         if (entry->id == 0)
284                 return;
285
286         for (ps = list_head_g; ps != NULL; ps = ps->next)
287         {
288                 if ((ps_list_match (name, cmdline, ps)) == 0)
289                         continue;
290
291                 for (pse = ps->instances; pse != NULL; pse = pse->next)
292                         if ((pse->id == entry->id) || (pse->next == NULL))
293                                 break;
294
295                 if ((pse == NULL) || (pse->id != entry->id))
296                 {
297                         procstat_entry_t *new;
298                         
299                         new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
300                         if (new == NULL)
301                                 return;
302                         memset (new, 0, sizeof (procstat_entry_t));
303                         new->id = entry->id;
304                         
305                         if (pse == NULL)
306                                 ps->instances = new;
307                         else
308                                 pse->next = new;
309
310                         pse = new;
311                 }
312
313                 pse->age = 0;
314                 pse->num_proc = entry->num_proc;
315                 pse->num_lwp  = entry->num_lwp;
316                 pse->vmem_rss = entry->vmem_rss;
317
318                 ps->num_proc += pse->num_proc;
319                 ps->num_lwp  += pse->num_lwp;
320                 ps->vmem_rss += pse->vmem_rss;
321
322                 if ((entry->vmem_minflt_counter == 0)
323                                 && (entry->vmem_majflt_counter == 0))
324                 {
325                         pse->vmem_minflt_counter += entry->vmem_minflt;
326                         pse->vmem_minflt = entry->vmem_minflt;
327
328                         pse->vmem_majflt_counter += entry->vmem_majflt;
329                         pse->vmem_majflt = entry->vmem_majflt;
330                 }
331                 else
332                 {
333                         if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
334                         {
335                                 pse->vmem_minflt = entry->vmem_minflt_counter
336                                         + (ULONG_MAX - pse->vmem_minflt_counter);
337                         }
338                         else
339                         {
340                                 pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
341                         }
342                         pse->vmem_minflt_counter = entry->vmem_minflt_counter;
343                         
344                         if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
345                         {
346                                 pse->vmem_majflt = entry->vmem_majflt_counter
347                                         + (ULONG_MAX - pse->vmem_majflt_counter);
348                         }
349                         else
350                         {
351                                 pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
352                         }
353                         pse->vmem_majflt_counter = entry->vmem_majflt_counter;
354                 }
355
356                 ps->vmem_minflt_counter += pse->vmem_minflt;
357                 ps->vmem_majflt_counter += pse->vmem_majflt;
358
359                 if ((entry->cpu_user_counter == 0)
360                                 && (entry->cpu_system_counter == 0))
361                 {
362                         pse->cpu_user_counter += entry->cpu_user;
363                         pse->cpu_user = entry->cpu_user;
364
365                         pse->cpu_system_counter += entry->cpu_system;
366                         pse->cpu_system = entry->cpu_system;
367                 }
368                 else
369                 {
370                         if (entry->cpu_user_counter < pse->cpu_user_counter)
371                         {
372                                 pse->cpu_user = entry->cpu_user_counter
373                                         + (ULONG_MAX - pse->cpu_user_counter);
374                         }
375                         else
376                         {
377                                 pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
378                         }
379                         pse->cpu_user_counter = entry->cpu_user_counter;
380                         
381                         if (entry->cpu_system_counter < pse->cpu_system_counter)
382                         {
383                                 pse->cpu_system = entry->cpu_system_counter
384                                         + (ULONG_MAX - pse->cpu_system_counter);
385                         }
386                         else
387                         {
388                                 pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
389                         }
390                         pse->cpu_system_counter = entry->cpu_system_counter;
391                 }
392
393                 ps->cpu_user_counter   += pse->cpu_user;
394                 ps->cpu_system_counter += pse->cpu_system;
395         }
396 }
397
398 /* remove old entries from instances of processes in list_head_g */
399 static void ps_list_reset (void)
400 {
401         procstat_t *ps;
402         procstat_entry_t *pse;
403         procstat_entry_t *pse_prev;
404
405         for (ps = list_head_g; ps != NULL; ps = ps->next)
406         {
407                 ps->num_proc    = 0;
408                 ps->num_lwp     = 0;
409                 ps->vmem_rss    = 0;
410
411                 pse_prev = NULL;
412                 pse = ps->instances;
413                 while (pse != NULL)
414                 {
415                         if (pse->age > 10)
416                         {
417                                 DEBUG ("Removing this procstat entry cause it's too old: "
418                                                 "id = %lu; name = %s;",
419                                                 pse->id, ps->name);
420
421                                 if (pse_prev == NULL)
422                                 {
423                                         ps->instances = pse->next;
424                                         free (pse);
425                                         pse = ps->instances;
426                                 }
427                                 else
428                                 {
429                                         pse_prev->next = pse->next;
430                                         free (pse);
431                                         pse = pse_prev->next;
432                                 }
433                         }
434                         else
435                         {
436                                 pse->age++;
437                                 pse_prev = pse;
438                                 pse = pse->next;
439                         }
440                 } /* while (pse != NULL) */
441         } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
442 }
443
444 /* put all pre-defined 'Process' names from config to list_head_g tree */
445 static int ps_config (const char *key, const char *value)
446 {
447         if (strcasecmp (key, "Process") == 0)
448         {
449                 ps_list_register (value, NULL);
450         }
451         else if (strcasecmp (key, "ProcessMatch") == 0)
452         {
453                 char *new_val;
454                 char *fields[3];
455                 int fields_num;
456
457                 new_val = strdup (value);
458                 if (new_val == NULL)
459                         return (1);
460                 fields_num = strsplit (new_val, fields,
461                                 STATIC_ARRAY_SIZE (fields));
462                 if (fields_num != 2)
463                 {
464                         sfree (new_val);
465                         return (1);
466                 }
467                 ps_list_register (fields[0], fields[1]);
468                 sfree (new_val);
469         }
470         else
471         {
472                 return (-1);
473         }
474
475         return (0);
476 }
477
478 static int ps_init (void)
479 {
480 #if HAVE_THREAD_INFO
481         kern_return_t status;
482
483         port_host_self = mach_host_self ();
484         port_task_self = mach_task_self ();
485
486         if (pset_list != NULL)
487         {
488                 vm_deallocate (port_task_self,
489                                 (vm_address_t) pset_list,
490                                 pset_list_len * sizeof (processor_set_t));
491                 pset_list = NULL;
492                 pset_list_len = 0;
493         }
494
495         if ((status = host_processor_sets (port_host_self,
496                                         &pset_list,
497                                         &pset_list_len)) != KERN_SUCCESS)
498         {
499                 ERROR ("host_processor_sets failed: %s\n",
500                                 mach_error_string (status));
501                 pset_list = NULL;
502                 pset_list_len = 0;
503                 return (-1);
504         }
505 /* #endif HAVE_THREAD_INFO */
506
507 #elif KERNEL_LINUX
508         pagesize_g = sysconf(_SC_PAGESIZE);
509         DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
510                         pagesize_g, CONFIG_HZ);
511 #endif /* KERNEL_LINUX */
512
513         return (0);
514 } /* int ps_init */
515
516 /* submit global state (e.g.: qty of zombies, running, etc..) */
517 static void ps_submit_state (const char *state, double value)
518 {
519         value_t values[1];
520         value_list_t vl = VALUE_LIST_INIT;
521
522         values[0].gauge = value;
523
524         vl.values = values;
525         vl.values_len = 1;
526         vl.time = time (NULL);
527         strcpy (vl.host, hostname_g);
528         strcpy (vl.plugin, "processes");
529         strcpy (vl.plugin_instance, "");
530         strcpy (vl.type, "ps_state");
531         sstrncpy (vl.type_instance, state, sizeof (vl.type_instance));
532
533         plugin_dispatch_values (&vl);
534 }
535
536 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
537 static void ps_submit_proc_list (procstat_t *ps)
538 {
539         value_t values[2];
540         value_list_t vl = VALUE_LIST_INIT;
541
542         vl.values = values;
543         vl.values_len = 2;
544         vl.time = time (NULL);
545         strcpy (vl.host, hostname_g);
546         strcpy (vl.plugin, "processes");
547         sstrncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
548
549         strcpy (vl.type, "ps_rss");
550         vl.values[0].gauge = ps->vmem_rss;
551         vl.values_len = 1;
552         plugin_dispatch_values (&vl);
553
554         strcpy (vl.type, "ps_cputime");
555         vl.values[0].counter = ps->cpu_user_counter;
556         vl.values[1].counter = ps->cpu_system_counter;
557         vl.values_len = 2;
558         plugin_dispatch_values (&vl);
559
560         strcpy (vl.type, "ps_count");
561         vl.values[0].gauge = ps->num_proc;
562         vl.values[1].gauge = ps->num_lwp;
563         vl.values_len = 2;
564         plugin_dispatch_values (&vl);
565
566         strcpy (vl.type, "ps_pagefaults");
567         vl.values[0].counter = ps->vmem_minflt_counter;
568         vl.values[1].counter = ps->vmem_majflt_counter;
569         vl.values_len = 2;
570         plugin_dispatch_values (&vl);
571
572         DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
573                         "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
574                         "cpu_user_counter = %lu; cpu_system_counter = %lu;",
575                         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
576                         ps->vmem_minflt_counter, ps->vmem_majflt_counter,
577                         ps->cpu_user_counter, ps->cpu_system_counter);
578 } /* void ps_submit_proc_list */
579
580 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
581 #if KERNEL_LINUX
582 static int *ps_read_tasks (int pid)
583 {
584         int *list = NULL;
585         int  list_size = 1; /* size of allocated space, in elements */
586         int  list_len = 0;  /* number of currently used elements */
587
588         char           dirname[64];
589         DIR           *dh;
590         struct dirent *ent;
591
592         ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid);
593
594         if ((dh = opendir (dirname)) == NULL)
595         {
596                 DEBUG ("Failed to open directory `%s'", dirname);
597                 return (NULL);
598         }
599
600         while ((ent = readdir (dh)) != NULL)
601         {
602                 if (!isdigit (ent->d_name[0]))
603                         continue;
604
605                 if ((list_len + 1) >= list_size)
606                 {
607                         int *new_ptr;
608                         int  new_size = 2 * list_size;
609                         /* Comes in sizes: 2, 4, 8, 16, ... */
610
611                         new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
612                         if (new_ptr == NULL)
613                         {
614                                 if (list != NULL)
615                                         free (list);
616                                 ERROR ("processes plugin: "
617                                                 "Failed to allocate more memory.");
618                                 return (NULL);
619                         }
620
621                         list = new_ptr;
622                         list_size = new_size;
623
624                         memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
625                 }
626
627                 list[list_len] = atoi (ent->d_name);
628                 if (list[list_len] != 0)
629                         list_len++;
630         }
631
632         closedir (dh);
633
634         if (list_len == 0)
635                 return (NULL);
636
637         assert (list_len < list_size);
638         assert (list[list_len] == 0);
639
640         return (list);
641 } /* int *ps_read_tasks */
642
643 int ps_read_process (int pid, procstat_t *ps, char *state)
644 {
645         char  filename[64];
646         char  buffer[1024];
647         FILE *fh;
648
649         char *fields[64];
650         char  fields_len;
651
652         int  *tasks;
653         int   i;
654
655         int   ppid;
656         int   name_len;
657
658         long long unsigned cpu_user_counter;
659         long long unsigned cpu_system_counter;
660         long long unsigned vmem_rss;
661
662         memset (ps, 0, sizeof (procstat_t));
663
664         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
665
666         if ((fh = fopen (filename, "r")) == NULL)
667                 return (-1);
668
669         if (fgets (buffer, 1024, fh) == NULL)
670         {
671                 fclose (fh);
672                 return (-1);
673         }
674
675         fclose (fh);
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: %s",
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
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 */
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 */