Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / cpu.c
index a5a136e..6b1b7a6 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
 #define COLLECTD_CPU_STATE_INTERRUPT 5
 #define COLLECTD_CPU_STATE_SOFTIRQ 6
 #define COLLECTD_CPU_STATE_STEAL 7
-#define COLLECTD_CPU_STATE_IDLE 8
-#define COLLECTD_CPU_STATE_ACTIVE 9 /* sum of (!idle) */
-#define COLLECTD_CPU_STATE_MAX 10   /* #states */
+#define COLLECTD_CPU_STATE_GUEST 8
+#define COLLECTD_CPU_STATE_GUEST_NICE 9
+#define COLLECTD_CPU_STATE_IDLE 10
+#define COLLECTD_CPU_STATE_ACTIVE 11 /* sum of (!idle) */
+#define COLLECTD_CPU_STATE_MAX 12    /* #states */
 
 #if HAVE_STATGRAB_H
 #include <statgrab.h>
 #error "No applicable input method."
 #endif
 
-static const char *cpu_state_names[] = {"user", "system",    "wait",    "nice",
-                                        "swap", "interrupt", "softirq", "steal",
-                                        "idle", "active"};
+static const char *cpu_state_names[] = {
+    "user",    "system", "wait",  "nice",       "swap", "interrupt",
+    "softirq", "steal",  "guest", "guest_nice", "idle", "active"};
 
 #ifdef PROCESSOR_CPU_LOAD_INFO
 static mach_port_t port_host;
@@ -132,6 +134,9 @@ static mach_msg_type_number_t cpu_list_len;
 /* #endif KERNEL_LINUX */
 
 #elif defined(HAVE_LIBKSTAT)
+#if HAVE_KSTAT_H
+#include <kstat.h>
+#endif
 /* colleague tells me that Sun doesn't sell systems with more than 100 or so
  * CPUs.. */
 #define MAX_NUMCPU 256
@@ -193,9 +198,12 @@ static _Bool report_by_cpu = 1;
 static _Bool report_by_state = 1;
 static _Bool report_percent = 0;
 static _Bool report_num_cpu = 0;
+static _Bool report_guest = 0;
+static _Bool subtract_guest = 1;
 
-static const char *config_keys[] = {"ReportByCpu", "ReportByState",
-                                    "ReportNumCpu", "ValuesPercentage"};
+static const char *config_keys[] = {"ReportByCpu",      "ReportByState",
+                                    "ReportNumCpu",     "ValuesPercentage",
+                                    "ReportGuestState", "SubtractGuestState"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
 
 static int cpu_config(char const *key, char const *value) /* {{{ */
@@ -208,6 +216,10 @@ static int cpu_config(char const *key, char const *value) /* {{{ */
     report_by_state = IS_TRUE(value) ? 1 : 0;
   else if (strcasecmp(key, "ReportNumCpu") == 0)
     report_num_cpu = IS_TRUE(value) ? 1 : 0;
+  else if (strcasecmp(key, "ReportGuestState") == 0)
+    report_guest = IS_TRUE(value) ? 1 : 0;
+  else if (strcasecmp(key, "SubtractGuestState") == 0)
+    subtract_guest = IS_TRUE(value) ? 1 : 0;
   else
     return -1;
 
@@ -326,7 +338,7 @@ static void submit_value(int cpu_num, int cpu_state, const char *type,
            sizeof(vl.type_instance));
 
   if (cpu_num >= 0) {
-    ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num);
+    snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num);
   }
   plugin_dispatch_values(&vl);
 }
@@ -524,7 +536,7 @@ static void cpu_commit_without_aggregation(void) /* {{{ */
 static void cpu_commit(void) /* {{{ */
 {
   gauge_t global_rates[COLLECTD_CPU_STATE_MAX] = {
-      NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
+      NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN /* Batman! */
   };
 
   if (report_num_cpu)
@@ -544,8 +556,8 @@ static void cpu_commit(void) /* {{{ */
 
   for (size_t cpu_num = 0; cpu_num < global_cpu_num; cpu_num++) {
     cpu_state_t *this_cpu_states = get_cpu_state(cpu_num, 0);
-    gauge_t local_rates[COLLECTD_CPU_STATE_MAX] = {NAN, NAN, NAN, NAN, NAN,
-                                                   NAN, NAN, NAN, NAN, NAN};
+    gauge_t local_rates[COLLECTD_CPU_STATE_MAX] = {
+        NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN};
 
     for (size_t state = 0; state < COLLECTD_CPU_STATE_MAX; state++)
       if (this_cpu_states[state].has_value)
@@ -632,7 +644,7 @@ static int cpu_read(void) {
   FILE *fh;
   char buf[1024];
 
-  char *fields[9];
+  char *fields[11];
   int numfields;
 
   if ((fh = fopen("/proc/stat", "r")) == NULL) {
@@ -648,14 +660,16 @@ static int cpu_read(void) {
     if ((buf[3] < '0') || (buf[3] > '9'))
       continue;
 
-    numfields = strsplit(buf, fields, 9);
+    numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE(fields));
     if (numfields < 5)
       continue;
 
     cpu = atoi(fields[0] + 3);
 
-    cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)atoll(fields[1]), now);
-    cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)atoll(fields[2]), now);
+    /* Do not stage User and Nice immediately: we may need to alter them later:
+     */
+    long long user_value = atoll(fields[1]);
+    long long nice_value = atoll(fields[2]);
     cpu_stage(cpu, COLLECTD_CPU_STATE_SYSTEM, (derive_t)atoll(fields[3]), now);
     cpu_stage(cpu, COLLECTD_CPU_STATE_IDLE, (derive_t)atoll(fields[4]), now);
 
@@ -665,11 +679,42 @@ static int cpu_read(void) {
                 now);
       cpu_stage(cpu, COLLECTD_CPU_STATE_SOFTIRQ, (derive_t)atoll(fields[7]),
                 now);
+    }
 
-      if (numfields >= 9)
-        cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]),
-                  now);
+    if (numfields >= 9) { /* Steal (since Linux 2.6.11) */
+      cpu_stage(cpu, COLLECTD_CPU_STATE_STEAL, (derive_t)atoll(fields[8]), now);
+    }
+
+    if (numfields >= 10) { /* Guest (since Linux 2.6.24) */
+      if (report_guest) {
+        long long value = atoll(fields[9]);
+        cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST, (derive_t)value, now);
+        /* Guest is included in User; optionally subtract Guest from User: */
+        if (subtract_guest) {
+          user_value -= value;
+          if (user_value < 0)
+            user_value = 0;
+        }
+      }
     }
+
+    if (numfields >= 11) { /* Guest_nice (since Linux 2.6.33) */
+      if (report_guest) {
+        long long value = atoll(fields[10]);
+        cpu_stage(cpu, COLLECTD_CPU_STATE_GUEST_NICE, (derive_t)value, now);
+        /* Guest_nice is included in Nice; optionally subtract Guest_nice from
+           Nice: */
+        if (subtract_guest) {
+          nice_value -= value;
+          if (nice_value < 0)
+            nice_value = 0;
+        }
+      }
+    }
+
+    /* Eventually stage User and Nice: */
+    cpu_stage(cpu, COLLECTD_CPU_STATE_USER, (derive_t)user_value, now);
+    cpu_stage(cpu, COLLECTD_CPU_STATE_NICE, (derive_t)nice_value, now);
   }
   fclose(fh);
 /* }}} #endif defined(KERNEL_LINUX) */