Home | History | Annotate | Download | only in hud
      1 /**************************************************************************
      2  *
      3  * Copyright (C) 2016 Steven Toth <stoth (at) kernellabs.com>
      4  * Copyright (C) 2016 Zodiac Inflight Innovations
      5  * All Rights Reserved.
      6  *
      7  * Permission is hereby granted, free of charge, to any person obtaining a
      8  * copy of this software and associated documentation files (the
      9  * "Software"), to deal in the Software without restriction, including
     10  * without limitation the rights to use, copy, modify, merge, publish,
     11  * distribute, sub license, and/or sell copies of the Software, and to
     12  * permit persons to whom the Software is furnished to do so, subject to
     13  * the following conditions:
     14  *
     15  * The above copyright notice and this permission notice (including the
     16  * next paragraph) shall be included in all copies or substantial portions
     17  * of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     22  * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
     23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  **************************************************************************/
     28 
     29 #if HAVE_GALLIUM_EXTRA_HUD
     30 
     31 /* Purpose:
     32  * Reading /sys/devices/system/cpu/cpu?/cpufreq/scaling_???_freq
     33  * cpu frequency (KHz), displaying on the HUD in Hz.
     34  */
     35 
     36 #include "hud/hud_private.h"
     37 #include "util/list.h"
     38 #include "os/os_time.h"
     39 #include "os/os_thread.h"
     40 #include "util/u_memory.h"
     41 #include <stdio.h>
     42 #include <unistd.h>
     43 #include <dirent.h>
     44 #include <stdlib.h>
     45 #include <errno.h>
     46 #include <inttypes.h>
     47 #include <sys/types.h>
     48 #include <sys/stat.h>
     49 
     50 struct cpufreq_info
     51 {
     52    struct list_head list;
     53    int mode; /* CPUFREQ_MINIMUM, CPUFREQ_CURRENT, CPUFREQ_MAXIMUM */
     54    char name[16]; /* EG. cpu0 */
     55    int cpu_index;
     56 
     57    /* EG. /sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq */
     58    char sysfs_filename[128];
     59    uint64_t KHz;
     60    uint64_t last_time;
     61 };
     62 
     63 static int gcpufreq_count = 0;
     64 static struct list_head gcpufreq_list;
     65 pipe_static_mutex(gcpufreq_mutex);
     66 
     67 static struct cpufreq_info *
     68 find_cfi_by_index(int cpu_index, int mode)
     69 {
     70    list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) {
     71       if (cfi->mode != mode)
     72          continue;
     73       if (cfi->cpu_index == cpu_index)
     74          return cfi;
     75    }
     76    return 0;
     77 }
     78 
     79 static int
     80 get_file_value(const char *fn, uint64_t *KHz)
     81 {
     82    FILE *fh = fopen(fn, "r");
     83    if (!fh) {
     84       fprintf(stderr, "%s error: %s\n", fn, strerror(errno));
     85       return -1;
     86    }
     87    int ret = fscanf(fh, "%" PRIu64 "", KHz);
     88    fclose(fh);
     89 
     90    return ret;
     91 }
     92 
     93 static void
     94 query_cfi_load(struct hud_graph *gr)
     95 {
     96    struct cpufreq_info *cfi = gr->query_data;
     97 
     98    uint64_t now = os_time_get();
     99    if (cfi->last_time) {
    100       if (cfi->last_time + gr->pane->period <= now) {
    101          switch (cfi->mode) {
    102          case CPUFREQ_MINIMUM:
    103          case CPUFREQ_CURRENT:
    104          case CPUFREQ_MAXIMUM:
    105             get_file_value(cfi->sysfs_filename, &cfi->KHz);
    106             hud_graph_add_value(gr, (uint64_t)cfi->KHz * 1000);
    107          }
    108          cfi->last_time = now;
    109       }
    110    } else {
    111       /* initialize */
    112       get_file_value(cfi->sysfs_filename, &cfi->KHz);
    113       cfi->last_time = now;
    114    }
    115 }
    116 
    117 /**
    118   * Create and initialize a new object for a specific CPU.
    119   * \param  pane  parent context.
    120   * \param  cpu_index  CPU identifier Eg. 0 (CPU0)
    121   * \param  mode  query CPUFREQ_MINIMUM | CURRENT | MAXIMUM statistic.
    122   */
    123 void
    124 hud_cpufreq_graph_install(struct hud_pane *pane, int cpu_index,
    125                            unsigned int mode)
    126 {
    127    struct hud_graph *gr;
    128    struct cpufreq_info *cfi;
    129 
    130    int num_cpus = hud_get_num_cpufreq(0);
    131    if (num_cpus <= 0)
    132       return;
    133 
    134    cfi = find_cfi_by_index(cpu_index, mode);
    135    if (!cfi)
    136       return;
    137 
    138    gr = CALLOC_STRUCT(hud_graph);
    139    if (!gr)
    140       return;
    141 
    142    cfi->mode = mode;
    143    switch(cfi->mode) {
    144    case CPUFREQ_MINIMUM:
    145       snprintf(gr->name, sizeof(gr->name), "%s-Min", cfi->name);
    146       break;
    147    case CPUFREQ_CURRENT:
    148       snprintf(gr->name, sizeof(gr->name), "%s-Cur", cfi->name);
    149       break;
    150    case CPUFREQ_MAXIMUM:
    151       snprintf(gr->name, sizeof(gr->name), "%s-Max", cfi->name);
    152       break;
    153    default:
    154       return;
    155    }
    156 
    157    gr->query_data = cfi;
    158    gr->query_new_value = query_cfi_load;
    159 
    160    hud_pane_add_graph(pane, gr);
    161    hud_pane_set_max_value(pane, 3000000 /* 3 GHz */);
    162 }
    163 
    164 static void
    165 add_object(const char *name, const char *fn, int objmode, int cpu_index)
    166 {
    167    struct cpufreq_info *cfi = CALLOC_STRUCT(cpufreq_info);
    168 
    169    strcpy(cfi->name, name);
    170    strcpy(cfi->sysfs_filename, fn);
    171    cfi->mode = objmode;
    172    cfi->cpu_index = cpu_index;
    173    list_addtail(&cfi->list, &gcpufreq_list);
    174    gcpufreq_count++;
    175 }
    176 
    177 /**
    178   * Initialize internal object arrays and display cpu freq HUD help.
    179   * \param  displayhelp  true if the list of detected cpus should be
    180                          displayed on the console.
    181   * \return  number of detected CPU metrics (CPU count * 3)
    182   */
    183 int
    184 hud_get_num_cpufreq(bool displayhelp)
    185 {
    186    struct dirent *dp;
    187    struct stat stat_buf;
    188    char fn[128];
    189    int cpu_index;
    190 
    191    /* Return the number of CPU metrics we support. */
    192    pipe_mutex_lock(gcpufreq_mutex);
    193    if (gcpufreq_count) {
    194       pipe_mutex_unlock(gcpufreq_mutex);
    195       return gcpufreq_count;
    196    }
    197 
    198    /* Scan /sys/devices.../cpu, for every object type we support, create
    199     * and persist an object to represent its different metrics.
    200     */
    201    list_inithead(&gcpufreq_list);
    202    DIR *dir = opendir("/sys/devices/system/cpu");
    203    if (!dir) {
    204       pipe_mutex_unlock(gcpufreq_mutex);
    205       return 0;
    206    }
    207 
    208    while ((dp = readdir(dir)) != NULL) {
    209 
    210       /* Avoid 'lo' and '..' and '.' */
    211       if (strlen(dp->d_name) <= 2)
    212          continue;
    213 
    214       if (sscanf(dp->d_name, "cpu%d\n", &cpu_index) != 1)
    215          continue;
    216 
    217       char basename[256];
    218       snprintf(basename, sizeof(basename), "/sys/devices/system/cpu/%s", dp->d_name);
    219 
    220       snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename);
    221       if (stat(fn, &stat_buf) < 0)
    222          continue;
    223 
    224       if (!S_ISREG(stat_buf.st_mode))
    225          continue;              /* Not a regular file */
    226 
    227       snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_min_freq", basename);
    228       add_object(dp->d_name, fn, CPUFREQ_MINIMUM, cpu_index);
    229 
    230       snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename);
    231       add_object(dp->d_name, fn, CPUFREQ_CURRENT, cpu_index);
    232 
    233       snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_max_freq", basename);
    234       add_object(dp->d_name, fn, CPUFREQ_MAXIMUM, cpu_index);
    235    }
    236    closedir(dir);
    237 
    238    if (displayhelp) {
    239       list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) {
    240          char line[128];
    241          snprintf(line, sizeof(line), "    cpufreq-%s-%s",
    242                  cfi->mode == CPUFREQ_MINIMUM ? "min" :
    243                  cfi->mode == CPUFREQ_CURRENT ? "cur" :
    244                  cfi->mode == CPUFREQ_MAXIMUM ? "max" : "undefined", cfi->name);
    245 
    246          puts(line);
    247       }
    248    }
    249 
    250    pipe_mutex_unlock(gcpufreq_mutex);
    251    return gcpufreq_count;
    252 }
    253 
    254 #endif /* HAVE_GALLIUM_EXTRA_HUD */
    255