collectd.conf(5): Added a note about that trailing whitespace bug.
[collectd.git] / src / processes.c
index 619b6e3..4b59351 100644 (file)
@@ -69,6 +69,9 @@
 #  if HAVE_MACH_VM_PROT_H
 #    include <mach/vm_prot.h>
 #  endif
+#  if HAVE_SYS_SYSCTL_H
+#    include <sys/sysctl.h>
+#  endif
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
@@ -141,12 +144,14 @@ static char *ps_pagefaults_ds_def[] =
 };
 static int ps_pagefaults_ds_num = 2;
 
+#if HAVE_THREAD_INFO | KERNEL_LINUX
 static char *config_keys[] =
 {
        "Process",
        NULL
 };
 static int config_keys_num = 1;
+#endif
 
 typedef struct procstat_entry_s
 {
@@ -189,7 +194,9 @@ typedef struct procstat
        struct procstat_entry_s *instances;
 } procstat_t;
 
+#if HAVE_THREAD_INFO | KERNEL_LINUX
 static procstat_t *list_head_g = NULL;
+#endif
 
 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -203,11 +210,7 @@ static mach_msg_type_number_t     pset_list_len;
 static long pagesize_g;
 #endif /* KERNEL_LINUX */
 
-#if HAVE_THREAD_INFO
-/* ps_list_* not used yet */
-/* #endif HAVE_THREAD_INFO */
-
-#elif KERNEL_LINUX
+#if HAVE_THREAD_INFO | KERNEL_LINUX
 static void ps_list_register (const char *name)
 {
        procstat_t *new;
@@ -404,7 +407,6 @@ static void ps_list_reset (void)
                } /* while (pse != NULL) */
        } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
 }
-#endif /* KERNEL_LINUX */
 
 static int ps_config (char *key, char *value)
 {
@@ -419,6 +421,7 @@ static int ps_config (char *key, char *value)
 
        return (0);
 }
+#endif /* HAVE_THREAD_INFO | KERNEL_LINUX */
 
 static void ps_init (void)
 {
@@ -577,8 +580,8 @@ static void ps_submit_proc_list (procstat_t *ps)
        plugin_submit ("ps_pagefaults", ps->name, buffer);
 
        DBG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
-                       "vmem_minflt_counter = %i; vmem_majflt_counter = %i; "
-                       "cpu_user_counter = %i; cpu_system_counter = %i;",
+                       "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
+                       "cpu_user_counter = %lu; cpu_system_counter = %lu;",
                        ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
                        ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
                        ps->cpu_system_counter);
@@ -601,8 +604,7 @@ static int *ps_read_tasks (int pid)
 
        if ((dh = opendir (dirname)) == NULL)
        {
-               syslog (LOG_NOTICE, "processes plugin: Failed to open directory `%s'",
-                               dirname);
+               DBG ("Failed to open directory `%s'", dirname);
                return (NULL);
        }
 
@@ -640,11 +642,14 @@ static int *ps_read_tasks (int pid)
 
        closedir (dh);
 
+       if (list_len == 0)
+               return (NULL);
+
        assert (list_len < list_size);
        assert (list[list_len] == 0);
 
        return (list);
-}
+} /* int *ps_read_tasks */
 
 int ps_read_process (int pid, procstat_t *ps, char *state)
 {
@@ -706,17 +711,21 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
 
        ppid = atoi (fields[3]);
 
-       if ((tasks = ps_read_tasks (pid)) == NULL)
-       {
-               /* This happends for zombied, e.g. */
-               DBG ("ps_read_tasks (%i) failed.", pid);
-               *state = 'Z';
-               ps->num_lwp  = 0;
-               ps->num_proc = 0;
-       }
+       *state = fields[2][0];
+       if (*state == 'Z')
+       {
+               ps->num_lwp  = 0;
+               ps->num_proc = 0;
+       }
+       else if ((tasks = ps_read_tasks (pid)) == NULL)
+       {
+               /* Kernel 2.4 or so */
+               ps->num_lwp  = 1;
+               ps->num_proc = 1;
+       }
        else
        {
-               *state = '\0';
                ps->num_lwp  = 0;
                ps->num_proc = 1;
                for (i = 0; tasks[i] != 0; i++)
@@ -726,10 +735,10 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                tasks = NULL;
        }
 
-       /* Leave the rest at zero if this is only an LWP */
+       /* Leave the rest at zero if this is only a zombi */
        if (ps->num_proc == 0)
        {
-               DBG ("This is only an LWP: pid = %i; name = %s;",
+               DBG ("This is only a zombi: pid = %i; name = %s;",
                                pid, ps->name);
                return (0);
        }
@@ -749,13 +758,47 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        ps->cpu_system_counter = (unsigned long) cpu_system_counter;
        ps->vmem_rss = (unsigned long) vmem_rss;
 
-       *state = fields[2][0];
-
        /* success */
        return (0);
 } /* int ps_read_process (...) */
 #endif /* KERNEL_LINUX */
 
+#if HAVE_THREAD_INFO
+static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
+{
+       int mib[4];
+
+       struct kinfo_proc kp;
+       size_t            kp_size;
+
+       mib[0] = CTL_KERN;
+       mib[1] = KERN_PROC;
+       mib[2] = KERN_PROC_PID;
+
+       if (pid_for_task (t, pid) != KERN_SUCCESS)
+               return (-1);
+       mib[3] = *pid;
+
+       kp_size = sizeof (kp);
+       if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
+               return (-1);
+
+       if (name_max_len > (MAXCOMLEN + 1))
+               name_max_len = MAXCOMLEN + 1;
+
+       strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
+       name[name_max_len - 1] = '\0';
+
+       DBG ("pid = %i; name = %s;", *pid, name);
+
+       /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
+        * `top' does it, because it is a lot of work and only used when
+        * debugging. -octo */
+
+       return (0);
+}
+#endif /* HAVE_THREAD_INFO */
+
 static void ps_read (void)
 {
 #if HAVE_THREAD_INFO
@@ -768,6 +811,9 @@ static void ps_read (void)
        task_array_t             task_list;
        mach_msg_type_number_t   task_list_len;
 
+       int                      task_pid;
+       char                     task_name[MAXCOMLEN + 1];
+
        int                      thread;
        thread_act_array_t       thread_list;
        mach_msg_type_number_t   thread_list_len;
@@ -780,6 +826,11 @@ static void ps_read (void)
        int stopped  = 0;
        int blocked  = 0;
 
+       procstat_t *ps;
+       procstat_entry_t pse;
+
+       ps_list_reset ();
+
        /*
         * The Mach-concept is a little different from the traditional UNIX
         * concept: All the work is done in threads. Threads are contained in
@@ -811,6 +862,71 @@ static void ps_read (void)
 
                for (task = 0; task < task_list_len; task++)
                {
+                       ps = NULL;
+                       if (mach_get_task_name (task_list[task],
+                                               &task_pid,
+                                               task_name, PROCSTAT_NAME_LEN) == 0)
+                               ps = ps_list_search (task_name);
+
+                       /* Collect more detailed statistics for this process */
+                       if (ps != NULL)
+                       {
+                               task_basic_info_data_t        task_basic_info;
+                               mach_msg_type_number_t        task_basic_info_len;
+                               task_events_info_data_t       task_events_info;
+                               mach_msg_type_number_t        task_events_info_len;
+                               task_absolutetime_info_data_t task_absolutetime_info;
+                               mach_msg_type_number_t        task_absolutetime_info_len;
+
+                               memset (&pse, '\0', sizeof (pse));
+                               pse.id = task_pid;
+
+                               task_basic_info_len = TASK_BASIC_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_BASIC_INFO,
+                                               (task_info_t) &task_basic_info,
+                                               &task_basic_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               task_events_info_len = TASK_EVENTS_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_EVENTS_INFO,
+                                               (task_info_t) &task_events_info,
+                                               &task_events_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_ABSOLUTETIME_INFO,
+                                               (task_info_t) &task_absolutetime_info,
+                                               &task_absolutetime_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               pse.num_proc++;
+                               pse.vmem_rss = task_basic_info.resident_size;
+
+                               pse.vmem_minflt_counter = task_events_info.cow_faults;
+                               pse.vmem_majflt_counter = task_events_info.faults;
+
+                               pse.cpu_user_counter = task_absolutetime_info.total_user;
+                               pse.cpu_system_counter = task_absolutetime_info.total_system;
+                       }
+
                        status = task_threads (task_list[task], &thread_list,
                                        &thread_list_len);
                        if (status != KERN_SUCCESS)
@@ -837,7 +953,7 @@ static void ps_read (void)
                                                &thread_data_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "thread_info failed: %s\n",
+                                       syslog (LOG_ERR, "thread_info failed: %s",
                                                        mach_error_string (status));
                                        if (task_list[task] != port_task_self)
                                                mach_port_deallocate (port_task_self,
@@ -845,6 +961,9 @@ static void ps_read (void)
                                        continue; /* with next thread_list */
                                }
 
+                               if (ps != NULL)
+                                       pse.num_lwp++;
+
                                switch (thread_data.run_state)
                                {
                                        case TH_STATE_RUNNING:
@@ -904,6 +1023,9 @@ static void ps_read (void)
                                        syslog (LOG_ERR, "mach_port_deallocate failed: %s",
                                                        mach_error_string (status));
                        }
+
+                       if (ps != NULL)
+                               ps_list_add (task_name, &pse);
                } /* for (task_list) */
 
                if ((status = vm_deallocate (port_task_self,
@@ -925,6 +1047,9 @@ static void ps_read (void)
        } /* for (pset_list) */
 
        ps_submit (running, sleeping, zombies, stopped, -1, blocked);
+
+       for (ps = list_head_g; ps != NULL; ps = ps->next)
+               ps_submit_proc_list (ps);
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
@@ -1019,7 +1144,9 @@ void module_register (void)
        plugin_register ("ps_cputime", NULL, NULL, ps_cputime_write);
        plugin_register ("ps_count", NULL, NULL, ps_count_write);
        plugin_register ("ps_pagefaults", NULL, NULL, ps_pagefaults_write);
+#if HAVE_THREAD_INFO | KERNEL_LINUX
        cf_register (MODULE_NAME, ps_config, config_keys, config_keys_num);
+#endif
 }
 
 #undef BUFSIZE