X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fcpufreq.c;h=35ec07fbc98c3ef798f200972310a1967ca5d08f;hb=c4439c9cb3e2348ad7013644731de27a55eca478;hp=a213a9000dcbbeeb4bd85a4b91793a8a326e7477;hpb=e3c4a74253fe8f47e4b074954abb75ff629cd060;p=collectd.git diff --git a/src/cpufreq.c b/src/cpufreq.c index a213a900..35ec07fb 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -22,85 +22,66 @@ #include "collectd.h" -#include "common.h" #include "plugin.h" +#include "utils/common/common.h" #define MAX_AVAIL_FREQS 20 static int num_cpu; -struct thread_data { - long long time_prev[MAX_AVAIL_FREQS]; - long long transitions; -} * t_data; +struct cpu_data_t { + value_to_rate_state_t time_state[MAX_AVAIL_FREQS]; +} * 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(*cpu_data)); + if (cpu_data == NULL) + return; - report_time_in_state = 1; - report_total_trans = 1; + report_p_stats = true; - /* Initialize time in state counters */ + /* Check for stats module and disable if not present. */ for (int i = 0; i < num_cpu; i++) { - char filename[256]; - FILE *fh; - int j = 0; - char state[DATA_MAX_NAME_LEN], buffer[DATA_MAX_NAME_LEN]; - long long t; + char filename[PATH_MAX]; snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i); - fh = fopen(filename, "r"); - if (fh == NULL) { - report_time_in_state = 0; - return 0; - } - while (fgets(buffer, sizeof(buffer), fh) != NULL) { - if (!sscanf(buffer, "%s%lli", state, &t)) { - fclose(fh); - return 0; - } - t_data[i].time_prev[j] = t; - j++; + 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; } - fclose(fh); - /* Initialize total transitions for cpu frequency */ snprintf(filename, sizeof(filename), "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i); - fh = fopen(filename, "r"); - if (fh == NULL) { - report_total_trans = 0; - return 0; - } - while (fgets(buffer, sizeof(buffer), fh) != NULL) { - if (!sscanf(buffer, "%lli", &t)) { - fclose(fh); - return 0; - } - t_data[i].transitions = t; + 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; } - fclose(fh); } - 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; @@ -111,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"); @@ -120,10 +101,10 @@ static int cpufreq_init(void) { } /* int cpufreq_init */ static void cpufreq_submit(int cpu_num, const char *type, - const char *type_instance, value_t value) { + const char *type_instance, value_t *value) { value_list_t vl = VALUE_LIST_INIT; - vl.values = &value; + vl.values = value; vl.values_len = 1; sstrncpy(vl.plugin, "cpufreq", sizeof(vl.plugin)); snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num); @@ -135,15 +116,81 @@ static void cpufreq_submit(int cpu_num, const char *type, plugin_dispatch_values(&vl); } +static void cpufreq_read_stats(int cpu) { + char filename[PATH_MAX]; + /* Read total transitions for cpu frequency */ + snprintf(filename, sizeof(filename), + "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", cpu); + + value_t v; + if (parse_value_file(filename, &v, DS_TYPE_DERIVE) != 0) { + ERROR("cpufreq plugin: Reading \"%s\" failed.", filename); + return; + } + cpufreq_submit(cpu, "transitions", NULL, &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", cpu); + + FILE *fh = fopen(filename, "r"); + if (fh == NULL) { + ERROR("cpufreq plugin: Reading \"%s\" failed.", filename); + return; + } + + int state_index = 0; + cdtime_t now = cdtime(); + char buffer[DATA_MAX_NAME_LEN]; + + while (fgets(buffer, sizeof(buffer), fh) != NULL) { + 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; + } + + char state[DATA_MAX_NAME_LEN]; + snprintf(state, sizeof(state), "%u", frequency); + + if (state_index >= MAX_AVAIL_FREQS) { + NOTICE("cpufreq plugin: Found too many frequency states (%d > %d). " + "Plugin needs to be recompiled. Please open a bug report for " + "this.", + (state_index + 1), MAX_AVAIL_FREQS); + break; + } + + gauge_t g; + if (value_to_rate(&g, (value_t){.derive = time}, DS_TYPE_DERIVE, now, + &(cpu_data[cpu].time_state[state_index])) == 0) { + /* + * Due to some inaccuracy reported value can be a bit greatrer than 100.1. + * That produces gaps on charts. + */ + if (g > 100.1) + g = 100.1; + cpufreq_submit(cpu, "percent", state, &(value_t){.gauge = g}); + } + state_index++; + } + fclose(fh); +} + 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) { @@ -154,63 +201,10 @@ static int cpufreq_read(void) { /* convert kHz to Hz */ v.gauge *= 1000.0; - cpufreq_submit(i, "cpufreq", NULL, v); - - /* Read total transitions for cpu frequency */ - if (report_total_trans) { - snprintf(filename, sizeof(filename), - "/sys/devices/system/cpu/cpu%d/cpufreq/stats/total_trans", i); - fh = fopen(filename, "r"); - if (fh == NULL) - continue; - while (fgets(buffer, sizeof(buffer), fh) != NULL) { - if (!sscanf(buffer, "%lli", &t)) { - fclose(fh); - return 0; - } - snprintf(buffer, sizeof(buffer), "%lli", t - t_data[i].transitions); - t_data[i].transitions = t; - } - if (parse_value(buffer, &v, DS_TYPE_GAUGE) != 0) { - WARNING("cpufreq plugin: Reading \"%s\" failed.", filename); - fclose(fh); - continue; - } - fclose(fh); - - cpufreq_submit(i, "transitions", NULL, v); - } + cpufreq_submit(cpu, "cpufreq", NULL, &v); - /* Determine time in state for cpu during previous interval - * Reported in 10mS as unit. - */ - if (report_time_in_state) { - int j = 0; - char state[DATA_MAX_NAME_LEN], time[DATA_MAX_NAME_LEN]; - value_t val; - - snprintf(filename, sizeof(filename), - "/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", i); - fh = fopen(filename, "r"); - if (fh == NULL) - continue; - while (fgets(buffer, sizeof(buffer), fh) != NULL) { - if (!sscanf(buffer, "%s%lli", state, &t)) { - fclose(fh); - return 0; - } - snprintf(time, sizeof(time), "%lli", t - t_data[i].time_prev[j]); - if (parse_value(time, &val, DS_TYPE_GAUGE) != 0) { - WARNING("cpufreq plugin: Reading \"%s\" failed.", filename); - fclose(fh); - continue; - } - cpufreq_submit(i, "time_in_state", state, val); - t_data[i].time_prev[j] = t; - j++; - } - fclose(fh); - } + if (report_p_stats) + cpufreq_read_stats(cpu); } return 0; } /* int cpufreq_read */