X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fprocesses.c;h=66237dc376be56414b29d62000d8a39c6fd70e1b;hb=6a5004f9c4394c8f561ba3b817196e7a50ad7237;hp=655cddb7d085a15a7e9e13e6dacabd20cb22d9a1;hpb=6bd4bf7b21f84746010792b885b9b71791dccb79;p=collectd.git diff --git a/src/processes.c b/src/processes.c index 655cddb7..66237dc3 100644 --- a/src/processes.c +++ b/src/processes.c @@ -121,13 +121,6 @@ # define ARG_MAX 4096 #endif -static const char *config_keys[] = -{ - "Process", - "ProcessMatch" -}; -static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); - typedef struct procstat_entry_s { unsigned long id; @@ -207,7 +200,7 @@ static long pagesize_g; /* #endif KERNEL_LINUX */ #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD -/* no global variables */ +static int pagesize; /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */ #elif HAVE_PROCINFO_H @@ -520,42 +513,61 @@ static void ps_list_reset (void) } /* put all pre-defined 'Process' names from config to list_head_g tree */ -static int ps_config (const char *key, const char *value) +static int ps_config (oconfig_item_t *ci) { - if (strcasecmp (key, "Process") == 0) - { - ps_list_register (value, NULL); - } - else if (strcasecmp (key, "ProcessMatch") == 0) - { - char *new_val; - char *fields[3]; - int fields_num; - - new_val = strdup (value); - if (new_val == NULL) { - ERROR ("processes plugin: strdup failed when processing " - "`ProcessMatch %s'.", value); - return (1); + int i; + + for (i = 0; i < ci->children_num; ++i) { + oconfig_item_t *c = ci->children + i; + + if (strcasecmp (c->key, "Process") == 0) + { + if ((c->values_num != 1) + || (OCONFIG_TYPE_STRING != c->values[0].type)) { + ERROR ("processes plugin: `Process' expects exactly " + "one string argument (got %i).", + c->values_num); + continue; + } + + if (c->children_num != 0) { + WARNING ("processes plugin: the `Process' config option " + "does not expect any child elements -- ignoring " + "content (%i elements) of the block.", + c->children_num, c->values[0].value.string); + } + + ps_list_register (c->values[0].value.string, NULL); } + else if (strcasecmp (c->key, "ProcessMatch") == 0) + { + if ((c->values_num != 2) + || (OCONFIG_TYPE_STRING != c->values[0].type) + || (OCONFIG_TYPE_STRING != c->values[1].type)) + { + ERROR ("processes plugin: `ProcessMatch' needs exactly " + "two string arguments (got %i).", + c->values_num); + continue; + } - fields_num = strsplit (new_val, fields, - STATIC_ARRAY_SIZE (fields)); - if (fields_num != 2) + if (c->children_num != 0) { + WARNING ("processes plugin: the `ProcessMatch' config option " + "does not expect any child elements -- ignoring " + "content (%i elements) of the " + "block.", c->children_num, c->values[0].value.string, + c->values[1].value.string); + } + + ps_list_register (c->values[0].value.string, + c->values[1].value.string); + } + else { - ERROR ("processes plugin: `ProcessMatch' needs exactly " - "two string arguments."); - sfree (new_val); - return (1); + ERROR ("processes plugin: The `%s' configuration option is not " + "understood and will be ignored.", c->key); + continue; } - ps_list_register (fields[0], fields[1]); - sfree (new_val); - } - else - { - ERROR ("processes plugin: The `%s' configuration option is not " - "understood and will be ignored.", key); - return (-1); } return (0); @@ -597,7 +609,7 @@ static int ps_init (void) /* #endif KERNEL_LINUX */ #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD -/* no initialization */ + pagesize = getpagesize(); /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */ #elif HAVE_PROCINFO_H @@ -778,7 +790,7 @@ static procstat_t *ps_read_vmem (int pid, procstat_t *ps) tmp = strtoll (fields[1], &endptr, /* base = */ 10); if ((errno == 0) && (endptr != fields[1])) { - if (strncmp (buffer, "VmData", 6) == 0) + if (strncmp (buffer, "VmData", 6) == 0) { data = tmp; } @@ -869,10 +881,12 @@ int ps_read_process (int pid, procstat_t *ps, char *state) char *fields[64]; char fields_len; - int i; + int buffer_len; - int ppid; - int name_len; + char *buffer_ptr; + size_t name_start_pos; + size_t name_end_pos; + size_t name_len; derive_t cpu_user_counter; derive_t cpu_system_counter; @@ -884,34 +898,56 @@ int ps_read_process (int pid, procstat_t *ps, char *state) ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid); - i = read_file_contents (filename, buffer, sizeof(buffer) - 1); - if (i <= 0) + buffer_len = read_file_contents (filename, + buffer, sizeof(buffer) - 1); + if (buffer_len <= 0) return (-1); - buffer[i] = 0; - - fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields)); - if (fields_len < 24) + buffer[buffer_len] = 0; + + /* The name of the process is enclosed in parens. Since the name can + * contain parens itself, spaces, numbers and pretty much everything + * else, use these to determine the process name. We don't use + * strchr(3) and strrchr(3) to avoid pointer arithmetic which would + * otherwise be required to determine name_len. */ + name_start_pos = 0; + while ((buffer[name_start_pos] != '(') + && (name_start_pos < buffer_len)) + name_start_pos++; + + name_end_pos = buffer_len; + while ((buffer[name_end_pos] != ')') + && (name_end_pos > 0)) + name_end_pos--; + + /* Either '(' or ')' is not found or they are in the wrong order. + * Anyway, something weird that shouldn't happen ever. */ + if (name_start_pos >= name_end_pos) { - DEBUG ("processes plugin: ps_read_process (pid = %i):" - " `%s' has only %i fields..", - (int) pid, filename, fields_len); + ERROR ("processes plugin: name_start_pos = %zu >= name_end_pos = %zu", + name_start_pos, name_end_pos); return (-1); } - /* copy the name, strip brackets in the process */ - name_len = strlen (fields[1]) - 2; - if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')')) + name_len = (name_end_pos - name_start_pos) - 1; + if (name_len >= sizeof (ps->name)) + name_len = sizeof (ps->name) - 1; + + sstrncpy (ps->name, &buffer[name_start_pos + 1], name_len + 1); + + if ((buffer_len - name_end_pos) < 2) + return (-1); + buffer_ptr = &buffer[name_end_pos + 2]; + + fields_len = strsplit (buffer_ptr, fields, STATIC_ARRAY_SIZE (fields)); + if (fields_len < 22) { - DEBUG ("No brackets found in process name: `%s'", fields[1]); + DEBUG ("processes plugin: ps_read_process (pid = %i):" + " `%s' has only %i fields..", + (int) pid, filename, fields_len); return (-1); } - fields[1] = fields[1] + 1; - fields[1][name_len] = '\0'; - strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN); - - ppid = atoi (fields[3]); - *state = fields[2][0]; + *state = fields[0][0]; if (*state == 'Z') { @@ -936,16 +972,16 @@ int ps_read_process (int pid, procstat_t *ps, char *state) return (0); } - cpu_user_counter = atoll (fields[13]); - cpu_system_counter = atoll (fields[14]); - vmem_size = atoll (fields[22]); - vmem_rss = atoll (fields[23]); - ps->vmem_minflt_counter = atoll (fields[9]); - ps->vmem_majflt_counter = atoll (fields[11]); + cpu_user_counter = atoll (fields[11]); + cpu_system_counter = atoll (fields[12]); + vmem_size = atoll (fields[20]); + vmem_rss = atoll (fields[21]); + ps->vmem_minflt_counter = atol (fields[7]); + ps->vmem_majflt_counter = atol (fields[9]); { - unsigned long long stack_start = atoll (fields[27]); - unsigned long long stack_ptr = atoll (fields[28]); + unsigned long long stack_start = atoll (fields[25]); + unsigned long long stack_ptr = atoll (fields[26]); stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr @@ -1569,10 +1605,8 @@ static int ps_read (void) kvm_t *kd; char errbuf[1024]; - char cmdline[ARG_MAX]; - char *cmdline_ptr; struct kinfo_proc *procs; /* array of processes */ - char **argv; + struct kinfo_proc *proc_ptr = NULL; int count; /* returns number of processes */ int i; @@ -1594,73 +1628,89 @@ static int ps_read (void) procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count); if (procs == NULL) { - kvm_close (kd); ERROR ("processes plugin: Cannot get kvm processes list: %s", kvm_geterr(kd)); + kvm_close (kd); return (0); } /* Iterate through the processes in kinfo_proc */ for (i = 0; i < count; i++) { - /* retrieve the arguments */ - cmdline[0] = 0; - cmdline_ptr = NULL; - - argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0); - if (argv != NULL) + /* Create only one process list entry per _process_, i.e. + * filter out threads (duplicate PID entries). */ + if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) { - int status; - int argc; - - argc = 0; - while (argv[argc] != NULL) - argc++; + char cmdline[ARG_MAX] = ""; + _Bool have_cmdline = 0; - status = strjoin (cmdline, sizeof (cmdline), - argv, argc, " "); - - if (status < 0) + proc_ptr = &(procs[i]); + /* Don't probe system processes and processes without arguments */ + if (((procs[i].ki_flag & P_SYSTEM) == 0) + && (procs[i].ki_args != NULL)) { - WARNING ("processes plugin: Command line did " - "not fit into buffer."); - } - else - { - cmdline_ptr = &cmdline[0]; - } - } + char **argv; + int argc; + int status; + + /* retrieve the arguments */ + argv = kvm_getargv (kd, proc_ptr, /* nchr = */ 0); + argc = 0; + if ((argv != NULL) && (argv[0] != NULL)) + { + while (argv[argc] != NULL) + argc++; + + status = strjoin (cmdline, sizeof (cmdline), argv, argc, " "); + if (status < 0) + WARNING ("processes plugin: Command line did not fit into buffer."); + else + have_cmdline = 1; + } + } /* if (process has argument list) */ - pse.id = procs[i].ki_pid; - pse.age = 0; + pse.id = procs[i].ki_pid; + pse.age = 0; - pse.num_proc = 1; - pse.num_lwp = procs[i].ki_numthreads; + pse.num_proc = 1; + pse.num_lwp = procs[i].ki_numthreads; - pse.vmem_size = procs[i].ki_size; - pse.vmem_rss = procs[i].ki_rssize * getpagesize(); - pse.vmem_data = procs[i].ki_dsize * getpagesize(); - pse.vmem_code = procs[i].ki_tsize * getpagesize(); - pse.stack_size = procs[i].ki_ssize * 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.vmem_size = procs[i].ki_size; + pse.vmem_rss = procs[i].ki_rssize * pagesize; + pse.vmem_data = procs[i].ki_dsize * pagesize; + pse.vmem_code = procs[i].ki_tsize * pagesize; + pse.stack_size = procs[i].ki_ssize * pagesize; + 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; + pse.cpu_user = 0; + pse.cpu_system = 0; + pse.cpu_user_counter = 0; + pse.cpu_system_counter = 0; + /* + * The u-area might be swapped out, and we can't get + * at it because we have a crashdump and no swap. + * If it's here fill in these fields, otherwise, just + * leave them 0. + */ + if (procs[i].ki_flag & P_INMEM) + { + pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec + + (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec); + pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_usec + + (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec); + } - /* no io data */ - pse.io_rchar = -1; - pse.io_wchar = -1; - pse.io_syscr = -1; - pse.io_syscw = -1; + /* no I/O data */ + pse.io_rchar = -1; + pse.io_wchar = -1; + pse.io_syscr = -1; + pse.io_syscw = -1; + + ps_list_add (procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse); + } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */ switch (procs[i].ki_stat) { @@ -1672,8 +1722,6 @@ static int ps_read (void) case SLOCK: blocked++; break; case SZOMB: zombies++; break; } - - ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse); } kvm_close(kd); @@ -1829,8 +1877,7 @@ static int ps_read (void) void module_register (void) { - plugin_register_config ("processes", ps_config, - config_keys, config_keys_num); + plugin_register_complex_config ("processes", ps_config); plugin_register_init ("processes", ps_init); plugin_register_read ("processes", ps_read); } /* void module_register */