cpufreq plugin: Address code review comments
[collectd.git] / src / cpufreq.c
index 18e79d4..6c2689f 100644 (file)
 
 static int num_cpu;
 
-struct thread_data {
+struct cpu_data_t {
   value_to_rate_state_t time_state[MAX_AVAIL_FREQS];
-  long long transition_prev;
-} * t_data;
+} * cpu_data;
 
-/* Flags denoting capability of reporting stats. */
-unsigned report_time_in_state, report_total_trans;
+/* Flags denoting capability of reporting CPU frequency statistics. */
+static bool report_p_stats = false;
 
-static int counter_init(void) {
-  t_data = calloc(num_cpu, sizeof(struct thread_data));
-  if (t_data == NULL)
-    return 0;
+static void cpufreq_stats_init(void) {
+  cpu_data = calloc(num_cpu, sizeof(struct cpu_data_t));
+  if (cpu_data == NULL)
+    return;
 
-  report_time_in_state = 1;
-  report_total_trans = 1;
+  report_p_stats = true;
 
   /* Check for stats module and disable if not present. */
   for (int i = 0; i < num_cpu; i++) {
-    char filename[256];
-    int status;
-    status = snprintf(filename, sizeof(filename),
-                      "/sys/devices/system/cpu/cpu%d/cpufreq/"
-                      "stats/time_in_state",
-                      num_cpu);
-    if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
-      report_time_in_state = 0;
+    char filename[PATH_MAX];
 
-    status = snprintf(filename, sizeof(filename),
-                      "/sys/devices/system/cpu/cpu%d/cpufreq/"
-                      "stats/total_transitions",
-                      num_cpu);
-    if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
-      report_total_trans = 0;
+    snprintf(filename, sizeof(filename),
+             "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i);
+    if (access(filename, R_OK)) {
+      NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+             "statistics will not be reported. Check if `cpufreq-stats' kernel "
+             "module is loaded.",
+             filename);
+      report_p_stats = false;
+      break;
+    }
 
-    /* Initialize total transitions for cpu frequency */
-    if (report_total_trans) {
-      value_t v;
-      snprintf(filename, sizeof(filename),
-               "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i);
-      if (parse_value_file(filename, &v, DS_TYPE_COUNTER) != 0) {
-        WARNING("cpufreq plugin: Reading \"%s\" failed.", filename);
-        continue;
-      }
-      t_data[i].transition_prev = v.counter;
+    snprintf(filename, sizeof(filename),
+             "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i);
+    if (access(filename, R_OK)) {
+      NOTICE("cpufreq plugin: File %s not exists or no access. P-State "
+             "statistics will not be reported. Check if `cpufreq-stats' kernel "
+             "module is loaded.",
+             filename);
+      report_p_stats = false;
+      break;
     }
   }
-  return 0;
+  return;
 }
 
 static int cpufreq_init(void) {
-  int status;
-  char filename[256];
+  char filename[PATH_MAX];
 
   num_cpu = 0;
 
   while (1) {
-    status = snprintf(filename, sizeof(filename),
-                      "/sys/devices/system/cpu/cpu%d/cpufreq/"
-                      "scaling_cur_freq",
-                      num_cpu);
+    int status = snprintf(filename, sizeof(filename),
+                          "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                          "scaling_cur_freq",
+                          num_cpu);
     if ((status < 1) || ((unsigned int)status >= sizeof(filename)))
       break;
 
@@ -99,7 +92,7 @@ static int cpufreq_init(void) {
   }
 
   INFO("cpufreq plugin: Found %d CPU%s", num_cpu, (num_cpu == 1) ? "" : "s");
-  counter_init();
+  cpufreq_stats_init();
 
   if (num_cpu == 0)
     plugin_unregister_read("cpufreq");
@@ -124,14 +117,11 @@ static void cpufreq_submit(int cpu_num, const char *type,
 }
 
 static int cpufreq_read(void) {
-  for (int i = 0; i < num_cpu; i++) {
+  for (int cpu = 0; cpu < num_cpu; cpu++) {
     char filename[PATH_MAX];
-    FILE *fh;
-    long long t;
-    char buffer[DATA_MAX_NAME_LEN];
     /* Read cpu frequency */
     snprintf(filename, sizeof(filename),
-             "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+             "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", cpu);
 
     value_t v;
     if (parse_value_file(filename, &v, DS_TYPE_GAUGE) != 0) {
@@ -142,54 +132,65 @@ static int cpufreq_read(void) {
     /* convert kHz to Hz */
     v.gauge *= 1000.0;
 
-    cpufreq_submit(i, "cpufreq", NULL, &v);
+    cpufreq_submit(cpu, "cpufreq", NULL, &v);
 
-    /* Read total transitions for cpu frequency */
-    if (report_total_trans) {
+    if (report_p_stats) {
+      /* Read total transitions for cpu frequency */
       snprintf(filename, sizeof(filename),
-               "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i);
+               "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", cpu);
       if (parse_value_file(filename, &v, DS_TYPE_COUNTER) != 0) {
-        WARNING("cpufreq plugin: Reading \"%s\" failed.", filename);
+        ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
         continue;
       }
-      counter_t c = counter_diff(t_data[i].transition_prev, v.counter);
-      t_data[i].transition_prev = v.counter;
-      cpufreq_submit(i, "transitions", NULL, &(value_t){.counter = c});
-    }
-
-    /*
-     * Determine percentage time in each state for cpu during previous interval.
-     */
-    if (report_time_in_state) {
-      int j = 0;
-      char state[DATA_MAX_NAME_LEN], time[DATA_MAX_NAME_LEN];
+      cpufreq_submit(cpu, "counter", "transitions", &v);
 
+      /* Determine percentage time in each state for cpu during previous
+       * interval. */
       snprintf(filename, sizeof(filename),
-               "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i);
-      fh = fopen(filename, "r");
-      if (fh == NULL)
+               "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state",
+               cpu);
+
+      FILE *fh = fopen(filename, "r");
+      if (fh == NULL) {
+        ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
         continue;
+      }
+
+      int state_index = 0;
+      cdtime_t now = cdtime();
+      char buffer[DATA_MAX_NAME_LEN];
+
       while (fgets(buffer, sizeof(buffer), fh) != NULL) {
-        if (!sscanf(buffer, "%s%lli", state, &t)) {
-          fclose(fh);
-          return 0;
+        unsigned int frequency;
+        unsigned long long time;
+
+        /*
+         * State time units is 10ms. To get rate of seconds per second
+         * we have to divide by 100. To get percents we have to multiply it
+         * by 100 back. So, just use parsed value directly.
+         */
+        if (!sscanf(buffer, "%u%llu", &frequency, &time)) {
+          ERROR("cpufreq plugin: Reading \"%s\" failed.", filename);
+          break;
         }
-        snprintf(time, sizeof(time), "%lli", t);
-        if (parse_value(time, &v, DS_TYPE_DERIVE) != 0) {
-          WARNING("cpufreq plugin: Reading \"%s\" failed.", filename);
-          continue;
+
+        char state[DATA_MAX_NAME_LEN];
+        snprintf(state, sizeof(state), "%u", frequency);
+
+        if (state_index >= MAX_AVAIL_FREQS) {
+          NOTICE("cpufreq plugin: Found too much frequency states (%d > %d). "
+                 "Plugin needs to be recompiled. Please open a bug report for "
+                 "this.",
+                 (state_index + 1), MAX_AVAIL_FREQS);
+          break;
         }
-        cdtime_t now = cdtime();
+
         gauge_t g;
-        if (j < MAX_AVAIL_FREQS) {
-          if (value_to_rate(&g, v, DS_TYPE_DERIVE, now,
-                            &t_data[i].time_state[j]) != 0) {
-            continue;
-            j++;
-          }
-          cpufreq_submit(i, "percent", state, &(value_t){.gauge = g});
+        if (value_to_rate(&g, (value_t){.counter = time}, DS_TYPE_COUNTER, now,
+                          &(cpu_data[cpu].time_state[state_index])) == 0) {
+          cpufreq_submit(cpu, "percent", state, &(value_t){.gauge = g});
         }
-        j++;
+        state_index++;
       }
       fclose(fh);
     }