From: Oleg King Date: Tue, 27 May 2008 14:48:51 +0000 (+0200) Subject: processes plugin: Add the `ProcessMatch' option. X-Git-Tag: collectd-4.5.0~143 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=49908d768d58345cd27bd2cf05f7c0c5f8b95a1e;p=collectd.git processes plugin: Add the `ProcessMatch' option. Hello! There is a patch for processes plugin: 1. Added support for libkvm, so processes plugin will be run under FreeBSD. 2. Added ProcessMatch "NAME" "REGEXP" directive, that adds process into list of processes that we watch on, but with identification by Regexp. Rexexp matched against command line, if command line is empty, than against task name. 3. Changed way we finding processes. Now we match not only first process, but all processes that match selected criteria. This allows to make some type of "grouping": Process "httpd" Process "dhclient" ProcessMatch "only-my-perl-program" "perl(.*)my-program\.pl" ProcessMatch "perl-programs" "^/usr/bin/perl" ProcessMatch "all-tasks" "(.*)" Patch is attached to this message. To octo: it is neede to make some thiungs I have not done: 1. make changes in manpages 2. make changes in config file 3. write rules that users should use when giving names with ProcessMatch --- diff --git a/configure.in b/configure.in index ce56227b..cac633ba 100644 --- a/configure.in +++ b/configure.in @@ -2448,6 +2448,7 @@ fi if test "x$with_libkvm" = "xyes" then + plugin_processes="yes" plugin_swap="yes" fi diff --git a/src/processes.c b/src/processes.c index 8275022f..d55c5f87 100644 --- a/src/processes.c +++ b/src/processes.c @@ -82,18 +82,32 @@ # endif /* #endif KERNEL_LINUX */ +#elif HAVE_KVM_H +# include +# include +# include +# if HAVE_SYS_SYSCTL_H +# include +# endif +/* #endif HAVE_KVM_H */ + #else # error "No applicable input method." #endif +#if HAVE_REGEX_H +# include +#endif + #define BUFSIZE 256 static const char *config_keys[] = { "Process", + "ProcessMatch", NULL }; -static int config_keys_num = 1; +static int config_keys_num = 2; typedef struct procstat_entry_s { @@ -121,6 +135,9 @@ typedef struct procstat_entry_s typedef struct procstat { char name[PROCSTAT_NAME_LEN]; +#if HAVE_REGEX_H + regex_t *re; +#endif unsigned long num_proc; unsigned long num_lwp; @@ -150,7 +167,10 @@ static mach_msg_type_number_t pset_list_len; static long pagesize_g; #endif /* KERNEL_LINUX */ -static void ps_list_register (const char *name) +/* put name of process from config to list_head_g tree + list_head_g is a list of 'procstat_t' structs with + processes names we want to watch */ +static void ps_list_register (const char *name, const char *regexp) { procstat_t *new; procstat_t *ptr; @@ -160,6 +180,25 @@ static void ps_list_register (const char *name) memset (new, 0, sizeof (procstat_t)); sstrncpy (new->name, name, sizeof (new->name)); + if (regexp != NULL) + { +#if HAVE_REGEX_H + DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name); + if ((new->re = (regex_t *) malloc (sizeof (regex_t))) != NULL) + { + if (regcomp(new->re, regexp, REG_EXTENDED|REG_NOSUB) != 0) + { + DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp); + sfree(new->re); + } + } else { + DEBUG("ProcessMatch: malloc failed when allocating memory for regexp!"); + } +#else + DEBUG("ProcessMatch: regexp '%s' met in config file, but regexps are not supported!", regexp); +#endif + } + for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) { if (strcmp (ptr->name, name) == 0) @@ -174,18 +213,19 @@ static void ps_list_register (const char *name) ptr->next = new; } -static procstat_t *ps_list_search (const char *name) +/* try to match name against entry, returns 1 if success */ +static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps) { - procstat_t *ptr; - - for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) - if (strcmp (ptr->name, name) == 0) - break; - - return (ptr); + if ((ps->re != NULL) && (regexec(ps->re, (strlen(cmdline)!=0)?cmdline:name, 0, NULL, 0) == 0)) + return (1); + if (strcmp (ps->name, name) == 0) { + return (1); + } + return (0); } -static void ps_list_add (const char *name, procstat_entry_t *entry) +/* add process entry to 'instances' of process 'name' (or refresh it) */ +static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry) { procstat_t *ps; procstat_entry_t *pse; @@ -193,115 +233,120 @@ static void ps_list_add (const char *name, procstat_entry_t *entry) if (entry->id == 0) return; - if ((ps = ps_list_search (name)) == NULL) - return; - - for (pse = ps->instances; pse != NULL; pse = pse->next) - if ((pse->id == entry->id) || (pse->next == NULL)) - break; - - if ((pse == NULL) || (pse->id != entry->id)) + for (ps = list_head_g; ps != NULL; ps = ps->next) { - procstat_entry_t *new; - new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t)); - if (new == NULL) - return; - memset (new, 0, sizeof (procstat_entry_t)); - new->id = entry->id; + if ((ps_list_match (name, cmdline, ps)) == 0) + continue; - if (pse == NULL) - ps->instances = new; - else - pse->next = new; + for (pse = ps->instances; pse != NULL; pse = pse->next) + if ((pse->id == entry->id) || (pse->next == NULL)) + break; - pse = new; - } + if ((pse == NULL) || (pse->id != entry->id)) + { + procstat_entry_t *new; + + new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t)); + if (new == NULL) + return; + memset (new, 0, sizeof (procstat_entry_t)); + new->id = entry->id; + + if (pse == NULL) + ps->instances = new; + else + pse->next = new; - pse->age = 0; - pse->num_proc = entry->num_proc; - pse->num_lwp = entry->num_lwp; - pse->vmem_rss = entry->vmem_rss; + pse = new; + } - ps->num_proc += pse->num_proc; - ps->num_lwp += pse->num_lwp; - ps->vmem_rss += pse->vmem_rss; + pse->age = 0; + pse->num_proc = entry->num_proc; + pse->num_lwp = entry->num_lwp; + pse->vmem_rss = entry->vmem_rss; - if ((entry->vmem_minflt_counter == 0) - && (entry->vmem_majflt_counter == 0)) - { - pse->vmem_minflt_counter += entry->vmem_minflt; - pse->vmem_minflt = entry->vmem_minflt; + ps->num_proc += pse->num_proc; + ps->num_lwp += pse->num_lwp; + ps->vmem_rss += pse->vmem_rss; - pse->vmem_majflt_counter += entry->vmem_majflt; - pse->vmem_majflt = entry->vmem_majflt; - } - else - { - if (entry->vmem_minflt_counter < pse->vmem_minflt_counter) - { - pse->vmem_minflt = entry->vmem_minflt_counter - + (ULONG_MAX - pse->vmem_minflt_counter); - } - else + if ((entry->vmem_minflt_counter == 0) + && (entry->vmem_majflt_counter == 0)) { - pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter; - } - pse->vmem_minflt_counter = entry->vmem_minflt_counter; + pse->vmem_minflt_counter += entry->vmem_minflt; + pse->vmem_minflt = entry->vmem_minflt; - if (entry->vmem_majflt_counter < pse->vmem_majflt_counter) - { - pse->vmem_majflt = entry->vmem_majflt_counter - + (ULONG_MAX - pse->vmem_majflt_counter); + pse->vmem_majflt_counter += entry->vmem_majflt; + pse->vmem_majflt = entry->vmem_majflt; } else { - pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter; + if (entry->vmem_minflt_counter < pse->vmem_minflt_counter) + { + pse->vmem_minflt = entry->vmem_minflt_counter + + (ULONG_MAX - pse->vmem_minflt_counter); + } + else + { + pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter; + } + pse->vmem_minflt_counter = entry->vmem_minflt_counter; + + if (entry->vmem_majflt_counter < pse->vmem_majflt_counter) + { + pse->vmem_majflt = entry->vmem_majflt_counter + + (ULONG_MAX - pse->vmem_majflt_counter); + } + else + { + pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter; + } + pse->vmem_majflt_counter = entry->vmem_majflt_counter; } - pse->vmem_majflt_counter = entry->vmem_majflt_counter; - } - - ps->vmem_minflt_counter += pse->vmem_minflt; - ps->vmem_majflt_counter += pse->vmem_majflt; - if ((entry->cpu_user_counter == 0) - && (entry->cpu_system_counter == 0)) - { - pse->cpu_user_counter += entry->cpu_user; - pse->cpu_user = entry->cpu_user; + ps->vmem_minflt_counter += pse->vmem_minflt; + ps->vmem_majflt_counter += pse->vmem_majflt; - pse->cpu_system_counter += entry->cpu_system; - pse->cpu_system = entry->cpu_system; - } - else - { - if (entry->cpu_user_counter < pse->cpu_user_counter) - { - pse->cpu_user = entry->cpu_user_counter - + (ULONG_MAX - pse->cpu_user_counter); - } - else + if ((entry->cpu_user_counter == 0) + && (entry->cpu_system_counter == 0)) { - pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter; - } - pse->cpu_user_counter = entry->cpu_user_counter; + pse->cpu_user_counter += entry->cpu_user; + pse->cpu_user = entry->cpu_user; - if (entry->cpu_system_counter < pse->cpu_system_counter) - { - pse->cpu_system = entry->cpu_system_counter - + (ULONG_MAX - pse->cpu_system_counter); + pse->cpu_system_counter += entry->cpu_system; + pse->cpu_system = entry->cpu_system; } else { - pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter; + if (entry->cpu_user_counter < pse->cpu_user_counter) + { + pse->cpu_user = entry->cpu_user_counter + + (ULONG_MAX - pse->cpu_user_counter); + } + else + { + pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter; + } + pse->cpu_user_counter = entry->cpu_user_counter; + + if (entry->cpu_system_counter < pse->cpu_system_counter) + { + pse->cpu_system = entry->cpu_system_counter + + (ULONG_MAX - pse->cpu_system_counter); + } + else + { + pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter; + } + pse->cpu_system_counter = entry->cpu_system_counter; } - pse->cpu_system_counter = entry->cpu_system_counter; - } - ps->cpu_user_counter += pse->cpu_user; - ps->cpu_system_counter += pse->cpu_system; + ps->cpu_user_counter += pse->cpu_user; + ps->cpu_system_counter += pse->cpu_system; + } } +/* remove old entries from instances of processes in list_head_g */ static void ps_list_reset (void) { procstat_t *ps; @@ -347,18 +392,36 @@ static void ps_list_reset (void) } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */ } +/* put all pre-defined 'Process' names from config to list_head_g tree */ static int ps_config (const char *key, const char *value) { + char *new_val; + char *fields[2]; + int fields_num; + if (strcasecmp (key, "Process") == 0) { - ps_list_register (value); + ps_list_register (value, NULL); + return (0); } - else + + if (strcasecmp (key, "ProcessMatch") == 0) { - return (-1); + new_val = strdup (value); + if (new_val == NULL) + return (-1); + fields_num = strsplit (new_val, fields, 2); + if (fields_num != 2) + { + sfree (new_val); + return (-1); + } + ps_list_register (fields[0], fields[1]); + sfree (new_val); + return (0); } - return (0); + return (-1); } static int ps_init (void) @@ -399,6 +462,7 @@ static int ps_init (void) return (0); } /* int ps_init */ +/* submit global state (e.g.: qty of zombies, running, etc..) */ static void ps_submit_state (const char *state, double value) { value_t values[1]; @@ -418,6 +482,7 @@ static void ps_submit_state (const char *state, double value) plugin_dispatch_values (&vl); } +/* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */ static void ps_submit_proc_list (procstat_t *ps) { value_t values[2]; @@ -461,6 +526,7 @@ static void ps_submit_proc_list (procstat_t *ps) ps->cpu_user_counter, ps->cpu_system_counter); } /* void ps_submit_proc_list */ +/* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */ #if KERNEL_LINUX static int *ps_read_tasks (int pid) { @@ -666,7 +732,9 @@ static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_l return (0); } #endif /* HAVE_THREAD_INFO */ +/* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */ +/* do actual readings from kernel */ static int ps_read (void) { #if HAVE_THREAD_INFO @@ -734,7 +802,12 @@ static int ps_read (void) if (mach_get_task_name (task_list[task], &task_pid, task_name, PROCSTAT_NAME_LEN) == 0) - ps = ps_list_search (task_name); + { + /* search for at least one match */ + for (ps = list_head_g; ps != NULL; ps = ps->next) + if (ps_list_match(task_name, NULL, ps) == 1) //!!! cmdline should be here instead of NULL + break; + } /* Collect more detailed statistics for this process */ if (ps != NULL) @@ -892,7 +965,7 @@ static int ps_read (void) } if (ps != NULL) - ps_list_add (task_name, &pse); + ps_list_add (task_name, NULL, &pse); //!!! cmdline should be here instead of NULL } /* for (task_list) */ if ((status = vm_deallocate (port_task_self, @@ -995,7 +1068,7 @@ static int ps_read (void) case 'W': paging++; break; } - ps_list_add (ps.name, &pse); + ps_list_add (ps.name, NULL, &pse); //!!! cmdline should be here instead of NULL } closedir (proc); @@ -1009,7 +1082,102 @@ static int ps_read (void) for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next) ps_submit_proc_list (ps_ptr); -#endif /* KERNEL_LINUX */ +/* #endif KERNEL_LINUX */ + +#elif HAVE_LIBKVM + int running = 0; + int sleeping = 0; + int zombies = 0; + int stopped = 0; + int blocked = 0; + int idle = 0; + int wait = 0; + + kvm_t *kd; + char errbuf[1024]; + char cmdline[ARG_MAX]; + struct kinfo_proc *procs; /* array of processes */ + char ** argv; + int count; /* returns number of processes */ + int i, j; + + procstat_t *ps_ptr; + procstat_entry_t pse; + + ps_list_reset (); + + /* Open the kvm interface, get a descriptor */ + if ((kd = kvm_open(NULL, NULL, NULL, 0, errbuf)) == NULL) { + ERROR ("Cannot open kvm interface: %s", errbuf); + return (0); + } + + /* Get the list of processes. */ + if ((procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count)) == NULL) { + kvm_close(kd); + ERROR ("Cannot get kvm processes list: %s", kvm_geterr(kd)); + return (0); + } + + /* Iterate through the processes in kinfo_proc */ + for (i=0; i < count; i++) { + // retrieve the arguments + *cmdline = '\0'; + argv = kvm_getargv(kd, (const struct kinfo_proc *) &(procs[i]), 0); + if (argv) { + j = 0; + while (argv[j] && strlen(cmdline) <= ARG_MAX) { + if (j) + strncat(cmdline, " ", 1); + strncat(cmdline, argv[j], strlen(argv[j])); + j++; + } + } + + pse.id = procs[i].ki_pid; + pse.age = 0; + + pse.num_proc = 1; + pse.num_lwp = procs[i].ki_numthreads; + + pse.vmem_rss = procs[i].ki_rssize * getpagesize(); + pse.vmem_minflt = 0; + pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt; + pse.vmem_majflt = 0; + pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt; + + pse.cpu_user = 0; + pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec*1000 + procs[i].ki_rusage.ru_utime.tv_usec; + pse.cpu_system = 0; + pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec*1000 + procs[i].ki_rusage.ru_stime.tv_usec; + + switch (procs[i].ki_stat) { + case SSTOP: stopped++; break; + case SSLEEP: sleeping++; break; + case SRUN: running++; break; + case SIDL: idle++; break; + case SWAIT: wait++; break; + case SLOCK: blocked++; break; + case SZOMB: zombies++; break; + } + + ps_list_add (procs[i].ki_comm, cmdline, &pse); + } + + if (kd) kvm_close(kd); + + ps_submit_state ("running", running); + ps_submit_state ("sleeping", sleeping); + ps_submit_state ("zombies", zombies); + ps_submit_state ("stopped", stopped); + ps_submit_state ("blocked", blocked); + ps_submit_state ("idle", idle); + ps_submit_state ("wait", wait); + + for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next) + ps_submit_proc_list (ps_ptr); + +#endif /* HAVE_LIBKVM */ return (0); } /* int ps_read */