From e4896436682865ad3a1baaadb1a0ac8f3abe8fb8 Mon Sep 17 00:00:00 2001 From: "Sexton, Rory" Date: Fri, 1 Jun 2018 15:43:48 +0100 Subject: [PATCH] Extending cpufreq plugin to read the following additional metrics: - number of p-state(cpu frequency) transitions - time spent in each p-state Both metrics are reported by the Linux cpufreq-stats driver: https://www.kernel.org/doc/Documentation/cpu-freq/cpufreq-stats.txt --- src/cpufreq.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/types.db | 2 + 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/cpufreq.c b/src/cpufreq.c index 851aad42..aa969b09 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -25,8 +25,71 @@ #include "common.h" #include "plugin.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; + +/* Flags denoting capability of reporting stats. */ +unsigned report_time_in_state, report_total_trans; + +static int counter_init(void){ + t_data = calloc(num_cpu, sizeof(struct thread_data)); + if (t_data == NULL) + return 0; + + report_time_in_state = 1; + report_total_trans = 1; + + /* Initialize time in state counters */ + 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; + + 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++; + } + 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; + } + fclose(fh); + } + return 0; +} + static int cpufreq_init(void) { int status; char filename[256]; @@ -48,6 +111,7 @@ static int cpufreq_init(void) { } INFO("cpufreq plugin: Found %d CPU%s", num_cpu, (num_cpu == 1) ? "" : "s"); + counter_init(); if (num_cpu == 0) plugin_unregister_read("cpufreq"); @@ -55,14 +119,17 @@ static int cpufreq_init(void) { return 0; } /* int cpufreq_init */ -static void cpufreq_submit(int cpu_num, value_t value) { +static void cpufreq_submit(int cpu_num, const char *type, const char *type_instance, value_t value) { value_list_t vl = VALUE_LIST_INIT; vl.values = &value; vl.values_len = 1; sstrncpy(vl.plugin, "cpufreq", sizeof(vl.plugin)); - sstrncpy(vl.type, "cpufreq", sizeof(vl.type)); - snprintf(vl.type_instance, sizeof(vl.type_instance), "%i", cpu_num); + snprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%i", cpu_num); + if (type != NULL) + sstrncpy(vl.type, type, sizeof(vl.type)); + if (type_instance != NULL) + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); plugin_dispatch_values(&vl); } @@ -70,6 +137,10 @@ static void cpufreq_submit(int cpu_num, value_t value) { static int cpufreq_read(void) { for (int i = 0; i < num_cpu; i++) { 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); @@ -82,9 +153,64 @@ static int cpufreq_read(void) { /* convert kHz to Hz */ v.gauge *= 1000.0; - cpufreq_submit(i, v); - } + 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); + } + /* 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); + } + } return 0; } /* int cpufreq_read */ diff --git a/src/types.db b/src/types.db index 0370b7f0..291467f6 100644 --- a/src/types.db +++ b/src/types.db @@ -244,6 +244,7 @@ tdp value:GAUGE:U:U temperature value:GAUGE:U:U threads value:GAUGE:0:U time_dispersion value:GAUGE:-1000000:1000000 +time_in_state value:GAUGE:0:U time_offset value:GAUGE:-1000000:1000000 time_offset_ntp value:GAUGE:-1000000:1000000 time_offset_rms value:GAUGE:-1000000:1000000 @@ -260,6 +261,7 @@ total_threads value:DERIVE:0:U total_time_in_ms value:DERIVE:0:U total_values value:DERIVE:0:U turbo_enabled value:GAUGE:0:1 +transitions value:GAUGE:0:U uptime value:GAUGE:0:4294967295 uncore_ratio value:GAUGE:0:U users value:GAUGE:0:65535 -- 2.11.0