From: Florian Forster Date: Tue, 19 Dec 2023 08:51:55 +0000 (+0100) Subject: src/processes.c: Importing changes from `main`. X-Git-Tag: 6.0.0-rc0~2^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=93ba05836c6fa0369ad7da839bc4dceb1ee05d59;p=thirdparty%2Fcollectd.git src/processes.c: Importing changes from `main`. Changes: d47b7020743e0ef942401b8cf143a0ac6476f9f5 clang format 71e99e00e21c8181408b590929f856830deac732 Compare both PID and process start time to avoid issues with immediate PID reuse on Linux platform. 9197134e7cd99206900f86f660be2135d028a2ab processes: format array size def in read fork_rate & ctxt a8b4168ac2237449e3192be3dd2d07ef587fa3e2 processes: fix prints & remove useless comments 2495f75ea8fc4ac0f295b0bdd38cb5eeb1ddd7c4 processes: eliminate hardcoded arraysize in read ctxt & fork_rate d2626382c6f0c506993ec13c4c2bcd69bad19371 processes: determine if ctxt is enabled before read_sys_ctxt_switch 0c41886ca975ed679e568210caa743af6bb75678 processes: format `/proc/stat` WARNING & ERROR 7a262979f76ebb5868320e09107bc02dc101ff07 processes: use buffer to read /proc/stat once according to procs_running ffc486a93f980b4d0d859b5f627f859d1877646c processes: remove running++ since we calc it in procs_running later c3bbd17e13558b15e09f0ecb374e44dfbf3623cd processes: make contextswitches configurable dfd034032b7c7c8f821774715c0723c42cefd332 Add scalloc() and use it in src/processes.c (#4014) --- diff --git a/src/processes.c b/src/processes.c index 54da9b356..fd460008e 100644 --- a/src/processes.c +++ b/src/processes.c @@ -177,6 +177,9 @@ typedef struct process_entry_s { unsigned long id; char name[PROCSTAT_NAME_LEN]; + // The time the process started after system boot. + // Value is in jiffies. + unsigned long long starttime; unsigned long num_proc; unsigned long num_lwp; @@ -220,6 +223,9 @@ typedef struct process_entry_s { typedef struct procstat_entry_s { unsigned long id; unsigned char age; + // The time the process started after system boot. + // Value is in jiffies. + unsigned long long starttime; derive_t vmem_minflt_counter; derive_t vmem_majflt_counter; @@ -303,6 +309,7 @@ static bool report_ctx_switch; static bool report_fd_num; static bool report_maps_num; static bool report_delay; +static bool report_sys_ctxt_switch; #if HAVE_THREAD_INFO static mach_port_t port_host_self; @@ -535,13 +542,20 @@ static void ps_list_add(const char *name, const char *cmdline, if ((pse->id == entry->id) || (pse->next == NULL)) break; - if ((pse == NULL) || (pse->id != entry->id)) { + if ((pse == NULL) || (pse->id != entry->id) || + (pse->starttime != entry->starttime)) { + if (pse != NULL && pse->id == entry->id) { + WARNING("pid %lu reused between two reads, ignoring existing " + "procstat_entry for %s", + pse->id, name); + } procstat_entry_t *new; new = calloc(1, sizeof(*new)); if (new == NULL) return; new->id = entry->id; + new->starttime = entry->starttime; if (pse == NULL) ps->instances = new; @@ -734,6 +748,8 @@ static int ps_config(oconfig_item_t *ci) { WARNING("processes plugin: The plugin has been compiled without support " "for the \"CollectDelayAccounting\" option."); #endif + } else if (strcasecmp(c->key, "CollectSystemContextSwitch") == 0) { + cf_util_get_boolean(c, &report_sys_ctxt_switch); } else { ERROR("processes plugin: The `%s' configuration option is not " "understood and will be ignored.", @@ -974,14 +990,14 @@ static void ps_submit_proc_list(procstat_t *ps) { } /* void ps_submit_proc_list */ #if KERNEL_LINUX || KERNEL_SOLARIS -static void ps_submit_fork_rate(derive_t value) { +static void ps_submit_global_stat(const char *type_name, derive_t value) { value_list_t vl = VALUE_LIST_INIT; vl.values = &(value_t){.derive = value}; vl.values_len = 1; sstrncpy(vl.plugin, "processes", sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance)); - sstrncpy(vl.type, "fork_rate", sizeof(vl.type)); + sstrncpy(vl.type, type_name, sizeof(vl.type)); sstrncpy(vl.type_instance, "", sizeof(vl.type_instance)); plugin_dispatch_values(&vl); @@ -1444,25 +1460,18 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) { ps->cswitch_vol = -1; ps->cswitch_invol = -1; + ps->starttime = strtoull(fields[19], NULL, 10); /* success */ return 0; } /* int ps_read_process (...) */ -static int procs_running(void) { - char buffer[65536] = {}; +static int procs_running(const char *buffer) { char id[] = "procs_running "; /* white space terminated */ char *running; char *endptr = NULL; long result = 0L; - ssize_t status; - - status = read_file_contents("/proc/stat", buffer, sizeof(buffer) - 1); - if (status <= 0) { - return -1; - } - /* the data contains : * the literal string 'procs_running', * a whitespace @@ -1471,7 +1480,7 @@ static int procs_running(void) { */ running = strstr(buffer, id); if (!running) { - WARNING("procs_running not found"); + WARNING("'procs_running ' not found in /proc/stat"); return -1; } running += strlen(id); @@ -1574,42 +1583,59 @@ static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) { return buf; } /* char *ps_get_cmdline (...) */ -static int read_fork_rate(void) { - FILE *proc_stat; - char buffer[1024]; +static int read_fork_rate(const char *buffer) { value_t value; - bool value_valid = 0; + char id[] = "processes "; + char *processes; - proc_stat = fopen("/proc/stat", "r"); - if (proc_stat == NULL) { - ERROR("processes plugin: fopen (/proc/stat) failed: %s", STRERRNO); + int status; + char *fields[2]; + int fields_num; + const int expected = STATIC_ARRAY_SIZE(fields); + + processes = strstr(buffer, id); + if (!processes) { + WARNING("'processes ' not found in /proc/stat"); return -1; } - while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) { - int status; - char *fields[3]; - int fields_num; + fields_num = strsplit(processes, fields, expected); + if (fields_num != expected) + return -1; - fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields)); - if (fields_num != 2) - continue; + status = parse_value(fields[1], &value, DS_TYPE_DERIVE); + if (status != 0) + return -1; - if (strcmp("processes", fields[0]) != 0) - continue; + ps_submit_global_stat("fork_rate", value.derive); + return 0; +} - status = parse_value(fields[1], &value, DS_TYPE_DERIVE); - if (status == 0) - value_valid = 1; +static int read_sys_ctxt_switch(const char *buffer) { + value_t value; + char id[] = "ctxt "; + char *ctxt; + + int status; + char *fields[2]; + int fields_num; + const int expected = STATIC_ARRAY_SIZE(fields); - break; + ctxt = strstr(buffer, id); + if (!ctxt) { + WARNING("'ctxt ' not found in /proc/stat"); + return -1; } - fclose(proc_stat); - if (!value_valid) + fields_num = strsplit(ctxt, fields, expected); + if (fields_num != expected) + return -1; + + status = parse_value(fields[1], &value, DS_TYPE_DERIVE); + if (status != 0) return -1; - ps_submit_fork_rate(value.derive); + ps_submit_global_stat("contextswitch", value.derive); return 0; } #endif /*KERNEL_LINUX */ @@ -1658,15 +1684,15 @@ static int ps_read_process(long pid, process_entry_t *ps, char *state) { snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid); snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid); - buffer = calloc(1, sizeof(pstatus_t)); + buffer = scalloc(1, sizeof(pstatus_t)); read_file_contents(filename, buffer, sizeof(pstatus_t)); myStatus = (pstatus_t *)buffer; - buffer = calloc(1, sizeof(psinfo_t)); + buffer = scalloc(1, sizeof(psinfo_t)); read_file_contents(f_psinfo, buffer, sizeof(psinfo_t)); myInfo = (psinfo_t *)buffer; - buffer = calloc(1, sizeof(prusage_t)); + buffer = scalloc(1, sizeof(prusage_t)); read_file_contents(f_usage, buffer, sizeof(prusage_t)); myUsage = (prusage_t *)buffer; @@ -1792,7 +1818,7 @@ static int read_fork_rate(void) { } } - ps_submit_fork_rate(result); + ps_submit_global_stat("fork_rate", result); return 0; } #endif /* KERNEL_SOLARIS */ @@ -2082,6 +2108,7 @@ static int ps_read(void) { DIR *proc; long pid; + char buffer[65536] = {}; char cmdline[CMDLINE_BUFFER_SIZE]; int status; @@ -2113,9 +2140,6 @@ static int ps_read(void) { } switch (state) { - case 'R': - running++; - break; case 'S': sleeping++; break; @@ -2139,6 +2163,10 @@ static int ps_read(void) { closedir(proc); + if (read_file_contents("/proc/stat", buffer, sizeof(buffer) - 1) <= 0) { + ERROR("Cannot read `/proc/stat`"); + return -1; + } /* get procs_running from /proc/stat * scanning /proc/stat AND computing other process stats takes too much time. * Consequently, the number of running processes based on the occurences @@ -2147,7 +2175,7 @@ static int ps_read(void) { * stat(s). * The 'procs_running' number in /proc/stat on the other hand is more * accurate, and can be retrieved in a single 'read' call. */ - running = procs_running(); + running = procs_running(buffer); ps_submit_state("running", running); ps_submit_state("sleeping", sleeping); @@ -2159,8 +2187,10 @@ static int ps_read(void) { for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next) ps_submit_proc_list(ps_ptr); - read_fork_rate(); - /* #endif KERNEL_LINUX */ + read_fork_rate(buffer); + if (report_sys_ctxt_switch) + read_sys_ctxt_switch(buffer); + /* #endif KERNEL_LINUX */ #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD int running = 0;