From e99b9395befe5b8612df3163b4deec2a0c0cb702 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 30 Sep 2016 05:58:00 -0600 Subject: [PATCH] gallium/hud: Add support for CPU frequency monitoring Detect all of the CPUs in the system. Expose metrics for min, max and current frequency in Hz. Signed-off-by: Steven Toth Reviewed-by: Brian Paul --- src/gallium/auxiliary/Makefile.sources | 1 + src/gallium/auxiliary/hud/hud_context.c | 13 ++ src/gallium/auxiliary/hud/hud_cpufreq.c | 266 ++++++++++++++++++++++++ src/gallium/auxiliary/hud/hud_private.h | 6 + 4 files changed, 286 insertions(+) create mode 100644 src/gallium/auxiliary/hud/hud_cpufreq.c diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources index 3d728aea82c..3fda2eb1733 100644 --- a/src/gallium/auxiliary/Makefile.sources +++ b/src/gallium/auxiliary/Makefile.sources @@ -63,6 +63,7 @@ C_SOURCES := \ hud/hud_context.h \ hud/hud_cpu.c \ hud/hud_nic.c \ + hud/hud_cpufreq.c \ hud/hud_diskstat.c \ hud/hud_sensors_temp.c \ hud/hud_driver_query.c \ diff --git a/src/gallium/auxiliary/hud/hud_context.c b/src/gallium/auxiliary/hud/hud_context.c index 31e81f05fe1..3772f3cc2a5 100644 --- a/src/gallium/auxiliary/hud/hud_context.c +++ b/src/gallium/auxiliary/hud/hud_context.c @@ -1044,6 +1044,18 @@ hud_parse_env_var(struct hud_context *hud, const char *env) hud_diskstat_graph_install(pane, arg_name, DISKSTAT_WR); pane->type = PIPE_DRIVER_QUERY_TYPE_BYTES; } + else if (sscanf(name, "cpufreq-min-cpu%u", &i) == 1) { + hud_cpufreq_graph_install(pane, i, CPUFREQ_MINIMUM); + pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; + } + else if (sscanf(name, "cpufreq-cur-cpu%u", &i) == 1) { + hud_cpufreq_graph_install(pane, i, CPUFREQ_CURRENT); + pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; + } + else if (sscanf(name, "cpufreq-max-cpu%u", &i) == 1) { + hud_cpufreq_graph_install(pane, i, CPUFREQ_MAXIMUM); + pane->type = PIPE_DRIVER_QUERY_TYPE_HZ; + } #endif #if HAVE_LIBSENSORS else if (sscanf(name, "sensors_temp_cu-%s", arg_name) == 1) { @@ -1284,6 +1296,7 @@ print_help(struct pipe_screen *screen) #if HAVE_GALLIUM_EXTRA_HUD hud_get_num_disks(1); hud_get_num_nics(1); + hud_get_num_cpufreq(1); #endif #if HAVE_LIBSENSORS hud_get_num_sensors(1); diff --git a/src/gallium/auxiliary/hud/hud_cpufreq.c b/src/gallium/auxiliary/hud/hud_cpufreq.c new file mode 100644 index 00000000000..1296ece84e2 --- /dev/null +++ b/src/gallium/auxiliary/hud/hud_cpufreq.c @@ -0,0 +1,266 @@ +/************************************************************************** + * + * Copyright (C) 2016 Steven Toth + * Copyright (C) 2016 Zodiac Inflight Innovations + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#if HAVE_GALLIUM_EXTRA_HUD + +/* Purpose: + * Reading /sys/devices/system/cpu/cpu?/cpufreq/scaling_???_freq + * cpu frequency (KHz), displaying on the HUD in Hz. + */ + +#include "hud/hud_private.h" +#include "util/list.h" +#include "os/os_time.h" +#include "util/u_memory.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOCAL_DEBUG 0 + +struct cpufreq_info +{ + struct list_head list; + int mode; /* CPUFREQ_MINIMUM, CPUFREQ_CURRENT, CPUFREQ_MAXIMUM */ + char name[16]; /* EG. cpu0 */ + int cpu_index; + + /* EG. /sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq */ + char sysfs_filename[128]; + uint64_t KHz; + uint64_t last_time; +}; + +static int gcpufreq_count = 0; +static struct list_head gcpufreq_list; + +static struct cpufreq_info * +find_cfi_by_index(int cpu_index, int mode) +{ + list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) { + if (cfi->mode != mode) + continue; + if (cfi->cpu_index == cpu_index) + return cfi; + } + return 0; +} + +static int +get_file_value(const char *fn, uint64_t *KHz) +{ + FILE *fh = fopen(fn, "r"); + if (!fh) { + fprintf(stderr, "%s error: %s\n", fn, strerror(errno)); + return -1; + } + int ret = fscanf(fh, "%" PRIu64 "", KHz); + fclose(fh); + + return ret; +} + +static void +query_cfi_load(struct hud_graph *gr) +{ + struct cpufreq_info *cfi = gr->query_data; + + uint64_t now = os_time_get(); + if (cfi->last_time) { + if (cfi->last_time + gr->pane->period <= now) { + switch (cfi->mode) { + case CPUFREQ_MINIMUM: + case CPUFREQ_CURRENT: + case CPUFREQ_MAXIMUM: + get_file_value(cfi->sysfs_filename, &cfi->KHz); + hud_graph_add_value(gr, (uint64_t)cfi->KHz * 1000); + } + cfi->last_time = now; + } + } else { + /* initialize */ + get_file_value(cfi->sysfs_filename, &cfi->KHz); + cfi->last_time = now; + } +} + +static void +free_query_data(void *p) +{ + struct cpufreq_info *cfi = (struct cpufreq_info *)p; + list_del(&cfi->list); + FREE(cfi); +} + +/** + * Create and initialize a new object for a specific CPU. + * \param pane parent context. + * \param cpu_index CPU identifier Eg. 0 (CPU0) + * \param mode query CPUFREQ_MINIMUM | CURRENT | MAXIMUM statistic. + */ +void +hud_cpufreq_graph_install(struct hud_pane *pane, int cpu_index, + unsigned int mode) +{ + struct hud_graph *gr; + struct cpufreq_info *cfi; + + int num_cpus = hud_get_num_cpufreq(0); + if (num_cpus <= 0) + return; + +#if LOCAL_DEBUG + printf("%s(%d, %s) - Creating HUD object\n", __func__, cpu_index, + mode == CPUFREQ_MINIMUM ? "MIN" : + mode == CPUFREQ_CURRENT ? "CUR" : + mode == CPUFREQ_MAXIMUM ? "MAX" : "UNDEFINED"); +#endif + + cfi = find_cfi_by_index(cpu_index, mode); + if (!cfi) + return; + + gr = CALLOC_STRUCT(hud_graph); + if (!gr) + return; + + cfi->mode = mode; + switch(cfi->mode) { + case CPUFREQ_MINIMUM: + snprintf(gr->name, sizeof(gr->name), "%s-Min", cfi->name); + break; + case CPUFREQ_CURRENT: + snprintf(gr->name, sizeof(gr->name), "%s-Cur", cfi->name); + break; + case CPUFREQ_MAXIMUM: + snprintf(gr->name, sizeof(gr->name), "%s-Max", cfi->name); + default: + return; + } + + gr->query_data = cfi; + gr->query_new_value = query_cfi_load; + + /* Don't use free() as our callback as that messes up Gallium's + * memory debugger. Use simple free_query_data() wrapper. + */ + gr->free_query_data = free_query_data; + + hud_pane_add_graph(pane, gr); + hud_pane_set_max_value(pane, 3000000 /* 3 GHz */); +} + +static void +add_object(const char *name, const char *fn, int objmode, int cpu_index) +{ + struct cpufreq_info *cfi = CALLOC_STRUCT(cpufreq_info); + + strcpy(cfi->name, name); + strcpy(cfi->sysfs_filename, fn); + cfi->mode = objmode; + cfi->cpu_index = cpu_index; + list_addtail(&cfi->list, &gcpufreq_list); + gcpufreq_count++; +} + +/** + * Initialize internal object arrays and display cpu freq HUD help. + * \param displayhelp true if the list of detected cpus should be + displayed on the console. + * \return number of detected CPU metrics (CPU count * 3) + */ +int +hud_get_num_cpufreq(bool displayhelp) +{ + struct dirent *dp; + struct stat stat_buf; + char fn[128]; + int cpu_index; + + /* Return the number of CPU metrics we support. */ + if (gcpufreq_count) + return gcpufreq_count; + + /* Scan /sys/devices.../cpu, for every object type we support, create + * and persist an object to represent its different metrics. + */ + list_inithead(&gcpufreq_list); + DIR *dir = opendir("/sys/devices/system/cpu"); + if (!dir) + return 0; + + while ((dp = readdir(dir)) != NULL) { + + /* Avoid 'lo' and '..' and '.' */ + if (strlen(dp->d_name) <= 2) + continue; + + if (sscanf(dp->d_name, "cpu%d\n", &cpu_index) != 1) + continue; + + char basename[256]; + snprintf(basename, sizeof(basename), "/sys/devices/system/cpu/%s", dp->d_name); + + snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename); + if (stat(fn, &stat_buf) < 0) + continue; + + if (!S_ISREG(stat_buf.st_mode)) + continue; /* Not a regular file */ + + snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_min_freq", basename); + add_object(dp->d_name, fn, CPUFREQ_MINIMUM, cpu_index); + + snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename); + add_object(dp->d_name, fn, CPUFREQ_CURRENT, cpu_index); + + snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_max_freq", basename); + add_object(dp->d_name, fn, CPUFREQ_MAXIMUM, cpu_index); + } + + if (displayhelp) { + list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) { + char line[128]; + snprintf(line, sizeof(line), " cpufreq-%s-%s", + cfi->mode == CPUFREQ_MINIMUM ? "min" : + cfi->mode == CPUFREQ_CURRENT ? "cur" : + cfi->mode == CPUFREQ_MAXIMUM ? "max" : "undefined", cfi->name); + + puts(line); + } + } + + return gcpufreq_count; +} + +#endif /* HAVE_GALLIUM_EXTRA_HUD */ diff --git a/src/gallium/auxiliary/hud/hud_private.h b/src/gallium/auxiliary/hud/hud_private.h index 51049afb98d..c6d0dbf66fb 100644 --- a/src/gallium/auxiliary/hud/hud_private.h +++ b/src/gallium/auxiliary/hud/hud_private.h @@ -116,6 +116,12 @@ int hud_get_num_disks(bool displayhelp); #define DISKSTAT_WR 2 void hud_diskstat_graph_install(struct hud_pane *pane, const char *dev_name, unsigned int mode); + +int hud_get_num_cpufreq(bool displayhelp); +#define CPUFREQ_MINIMUM 1 +#define CPUFREQ_CURRENT 2 +#define CPUFREQ_MAXIMUM 3 +void hud_cpufreq_graph_install(struct hud_pane *pane, int cpu_index, unsigned int mode); #endif #if HAVE_LIBSENSORS -- 2.30.2