From 5ad4d81af38cc11bdaaf87270bce2d8f90494fd5 Mon Sep 17 00:00:00 2001 From: octo Date: Sat, 8 Apr 2006 16:30:42 +0000 Subject: [PATCH] Second implementation of the `processes' plugin for Mac OS X - this time with processor sets, tasks lists, threads and everything. Mach rules :) --- src/processes.c | 384 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 266 insertions(+), 118 deletions(-) diff --git a/src/processes.c b/src/processes.c index 18bcec95..22d8c045 100644 --- a/src/processes.c +++ b/src/processes.c @@ -26,13 +26,50 @@ #include "plugin.h" #include "utils_debug.h" -#if HAVE_SYS_SYSCTL_H -# include +/* Include header files for the mach system, if they exist.. */ +#if HAVE_MACH_MACH_INIT_H +# include +#endif +#if HAVE_MACH_HOST_PRIV_H +# include +#endif +#if HAVE_MACH_MACH_ERROR_H +# include +#endif +#if HAVE_MACH_MACH_HOST_H +# include +#endif +#if HAVE_MACH_MACH_PORT_H +# include +#endif +#if HAVE_MACH_MACH_TYPES_H +# include +#endif +#if HAVE_MACH_MESSAGE_H +# include +#endif +#if HAVE_MACH_PROCESSOR_SET_H +# include +#endif +#if HAVE_MACH_TASK_H +# include +#endif +#if HAVE_MACH_THREAD_ACT_H +# include +#endif +#if HAVE_MACH_VM_REGION_H +# include +#endif +#if HAVE_MACH_VM_MAP_H +# include +#endif +#if HAVE_MACH_VM_PROT_H +# include #endif #define MODULE_NAME "processes" -#if defined(KERNEL_LINUX) || defined(HAVE_SYSCTLBYNAME) +#if HAVE_THREAD_INFO || defined(KERNEL_LINUX) # define PROCESSES_HAVE_READ 1 #else # define PROCESSES_HAVE_READ 0 @@ -54,8 +91,52 @@ static char *ds_def[] = }; static int ds_num = 6; +#if HAVE_THREAD_INFO +static mach_port_t port_host_self; +static mach_port_t port_task_self; + +static processor_set_name_array_t pset_list; +static mach_msg_type_number_t pset_list_len; +/* #endif HAVE_THREAD_INFO */ + +#elif KERNEL_LINUX +/* No global variables */ +#endif /* KERNEL_LINUX */ + static void ps_init (void) { +#if HAVE_THREAD_INFO + kern_return_t status; + + port_host_self = mach_host_self (); + port_task_self = mach_task_self (); + + if (pset_list != NULL) + { + vm_deallocate (port_task_self, + (vm_address_t) pset_list, + pset_list_len * sizeof (processor_set_t)); + pset_list = NULL; + pset_list_len = 0; + } + + if ((status = host_processor_sets (port_host_self, + &pset_list, + &pset_list_len)) != KERN_SUCCESS) + { + syslog (LOG_ERR, "host_processor_sets failed: %s\n", + mach_error_string (status)); + pset_list = NULL; + pset_list_len = 0; + return; + } +/* #endif HAVE_THREAD_INFO */ + +#elif KERNEL_LINUX + /* No init */ +#endif /* KERNEL_LINUX */ + + return; } static void ps_write (char *host, char *inst, char *val) @@ -64,16 +145,16 @@ static void ps_write (char *host, char *inst, char *val) } #if PROCESSES_HAVE_READ -static void ps_submit (unsigned int running, - unsigned int sleeping, - unsigned int zombies, - unsigned int stopped, - unsigned int paging, - unsigned int blocked) +static void ps_submit (int running, + int sleeping, + int zombies, + int stopped, + int paging, + int blocked) { char buf[BUFSIZE]; - if (snprintf (buf, BUFSIZE, "%u:%u:%u:%u:%u:%u:%u", + if (snprintf (buf, BUFSIZE, "%u:%i:%i:%i:%i:%i:%i", (unsigned int) curtime, running, sleeping, zombies, stopped, paging, blocked) >= BUFSIZE) @@ -84,8 +165,180 @@ static void ps_submit (unsigned int running, static void ps_read (void) { -#ifdef KERNEL_LINUX - unsigned int running, sleeping, zombies, stopped, paging, blocked; +#if HAVE_THREAD_INFO + kern_return_t status; + + int pset; + processor_set_t port_pset_priv; + + int task; + task_array_t task_list; + mach_msg_type_number_t task_list_len; + + int thread; + thread_act_array_t thread_list; + mach_msg_type_number_t thread_list_len; + thread_basic_info_data_t thread_data; + mach_msg_type_number_t thread_data_len; + + int running = 0; + int sleeping = 0; + int zombies = 0; + int stopped = 0; + int blocked = 0; + + /* + * The Mach-concept is a little different from the traditional UNIX + * concept: All the work is done in threads. Threads are contained in + * `tasks'. Therefore, `task status' doesn't make much sense, since + * it's actually a `thread status'. + * Tasks are assigned to sets of processors, so that's where you go to + * get a list. + */ + for (pset = 0; pset < pset_list_len; pset++) + { + if ((status = host_processor_set_priv (port_host_self, + pset_list[pset], + &port_pset_priv)) != KERN_SUCCESS) + { + syslog (LOG_ERR, "host_processor_set_priv failed: %s\n", + mach_error_string (status)); + continue; + } + + if ((status = processor_set_tasks (port_pset_priv, + &task_list, + &task_list_len)) != KERN_SUCCESS) + { + syslog (LOG_ERR, "processor_set_tasks failed: %s\n", + mach_error_string (status)); + mach_port_deallocate (port_task_self, port_pset_priv); + continue; + } + + for (task = 0; task < task_list_len; task++) + { + status = task_threads (task_list[task], &thread_list, + &thread_list_len); + if (status != KERN_SUCCESS) + { + /* Apple's `top' treats this case a zombie. It + * makes sense to some extend: A `zombie' + * thread is nonsense, since the task/process + * is dead. */ + zombies++; + DBG ("task_threads failed: %s", + mach_error_string (status)); + if (task_list[task] != port_task_self) + mach_port_deallocate (port_task_self, + task_list[task]); + continue; /* with next task_list */ + } + + for (thread = 0; thread < thread_list_len; thread++) + { + thread_data_len = THREAD_BASIC_INFO_COUNT; + status = thread_info (thread_list[thread], + THREAD_BASIC_INFO, + (thread_info_t) &thread_data, + &thread_data_len); + if (status != KERN_SUCCESS) + { + syslog (LOG_ERR, "thread_info failed: %s\n", + mach_error_string (status)); + if (task_list[task] != port_task_self) + mach_port_deallocate (port_task_self, + thread_list[thread]); + continue; /* with next thread_list */ + } + + switch (thread_data.run_state) + { + case TH_STATE_RUNNING: + running++; + break; + case TH_STATE_STOPPED: + /* What exactly is `halted'? */ + case TH_STATE_HALTED: + stopped++; + break; + case TH_STATE_WAITING: + sleeping++; + break; + case TH_STATE_UNINTERRUPTIBLE: + blocked++; + break; + /* There is no `zombie' case here, + * since there are no zombie-threads. + * There's only zombie tasks, which are + * handled above. */ + default: + syslog (LOG_WARNING, + "Unknown thread status: %s", + thread_data.run_state); + break; + } /* switch (thread_data.run_state) */ + + if (task_list[task] != port_task_self) + { + status = mach_port_deallocate (port_task_self, + thread_list[thread]); + if (status != KERN_SUCCESS) + syslog (LOG_ERR, "mach_port_deallocate failed: %s", + mach_error_string (status)); + } + } /* for (thread_list) */ + + if ((status = vm_deallocate (port_task_self, + (vm_address_t) thread_list, + thread_list_len * sizeof (thread_act_t))) + != KERN_SUCCESS) + { + syslog (LOG_ERR, "vm_deallocate failed: %s", + mach_error_string (status)); + } + thread_list = NULL; + thread_list_len = 0; + + /* Only deallocate the task port, if it isn't our own. + * Don't know what would happen in that case, but this + * is what Apple's top does.. ;) */ + if (task_list[task] != port_task_self) + { + status = mach_port_deallocate (port_task_self, + task_list[task]); + if (status != KERN_SUCCESS) + syslog (LOG_ERR, "mach_port_deallocate failed: %s", + mach_error_string (status)); + } + } /* for (task_list) */ + + if ((status = vm_deallocate (port_task_self, + (vm_address_t) task_list, + task_list_len * sizeof (task_t))) != KERN_SUCCESS) + { + syslog (LOG_ERR, "vm_deallocate failed: %s", + mach_error_string (status)); + } + task_list = NULL; + task_list_len = 0; + + if ((status = mach_port_deallocate (port_task_self, port_pset_priv)) + != KERN_SUCCESS) + { + syslog (LOG_ERR, "mach_port_deallocate failed: %s", + mach_error_string (status)); + } + } /* for (pset_list) */ +/* #endif HAVE_THREAD_INFO */ + +#elif KERNEL_LINUX + int running = 0; + int sleeping = 0; + int zombies = 0; + int stopped = 0; + int paging = 0; + int blocked = 0; char buf[BUFSIZE]; char filename[20]; /* need 17 bytes */ @@ -142,112 +395,7 @@ static void ps_read (void) closedir(proc); ps_submit (running, sleeping, zombies, stopped, paging, blocked); -/* #endif defined(KERNEL_LINUX) */ - -#elif HAVE_SYSCTLBYNAME - int mib[3]; - size_t len; - size_t num; - int i; - int tries; - struct kinfo_proc *kp; - - unsigned int state_idle = 0; - unsigned int state_run = 0; - unsigned int state_sleep = 0; - unsigned int state_stop = 0; - unsigned int state_zombie = 0; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_ALL; - - tries = 0; - kp = NULL; - while (1) - { - if (tries >= 3) - return; - tries++; - - len = 0; - if (sysctl(mib, 3, NULL, &len, NULL, 0) != 0) - { - syslog (LOG_ERR, "processes: sysctl failed: %s", - strerror (errno)); - return; - } - - if ((kp = (struct kinfo_proc *) malloc (len)) == NULL) - { - syslog (LOG_ERR, "processes: malloc failed: %s", - strerror (errno)); - return; - } - - if (sysctl(mib, 3, (void *) kp, &len, NULL, 0) != 0) - { - syslog (LOG_WARNING, "processes: sysctl failed: %s", - strerror (errno)); - free (kp); - kp = NULL; - continue; - } - - break; - } /* while true */ - - /* If we get past the while-loop, `kp' containes a valid `struct - * kinfo_proc'. */ - - num = len / sizeof (struct kinfo_proc); - - for (i = 0; i < num; i++) - { - DBG ("%3i: Process %i is in state %i", i, - (int) kp[i].kp_proc.p_pid, - (int) kp[i].kp_proc.p_stat); - - switch (kp[i].kp_proc.p_stat) - { - case SIDL: - state_idle++; - break; - - case SRUN: - state_run++; - break; - - case SSLEEP: -#ifdef P_SINTR - if ((kp[i].kp_proc.p_flag & P_SINTR) == 0) - state_sleep++; /* TODO change this to `state_blocked' or something.. */ - else -#endif /* P_SINTR */ - state_sleep++; - break; - - case SSTOP: - state_stop++; - break; - - case SZOMB: - state_zombie++; - break; - - default: - syslog (LOG_WARNING, "processes: PID %i in unknown state 0x%2x", - (int) kp[i].kp_proc.p_pid, - (int) kp[i].kp_proc.p_stat); - } /* switch (state) */ - } /* for (i = 0 .. num-1) */ - - free (kp); - - if (state_run || state_idle || state_sleep || state_zombie) - ps_submit (state_run, state_idle + state_sleep, state_zombie, - state_stop, -1, -1); -#endif /* HAVE_SYSCTLBYNAME */ +#endif /* KERNEL_LINUX */ } #else # define ps_read NULL -- 2.30.2