]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
src/processes.c: Importing changes from `main`.
authorFlorian Forster <octo@collectd.org>
Tue, 19 Dec 2023 08:51:55 +0000 (09:51 +0100)
committerFlorian Forster <octo@collectd.org>
Mon, 22 Jan 2024 20:22:04 +0000 (21:22 +0100)
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)

src/processes.c

index 54da9b3561b9eb6b49fee7afcf66111d405867e7..fd460008ed58402ac16739c752949cfe494770a1 100644 (file)
 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;