Home | History | Annotate | Download | only in hud
      1 /**************************************************************************
      2  *
      3  * Copyright 2013 Marek Olk <maraeo (at) gmail.com>
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 /* This file contains code for reading CPU load for displaying on the HUD.
     29  */
     30 
     31 #include "hud/hud_private.h"
     32 #include "os/os_time.h"
     33 #include "util/u_memory.h"
     34 #include <stdio.h>
     35 #include <inttypes.h>
     36 #ifdef PIPE_OS_WINDOWS
     37 #include <windows.h>
     38 #endif
     39 
     40 
     41 #ifdef PIPE_OS_WINDOWS
     42 
     43 static inline uint64_t
     44 filetime_to_scalar(FILETIME ft)
     45 {
     46    ULARGE_INTEGER uli;
     47    uli.LowPart = ft.dwLowDateTime;
     48    uli.HighPart = ft.dwHighDateTime;
     49    return uli.QuadPart;
     50 }
     51 
     52 static boolean
     53 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
     54 {
     55    SYSTEM_INFO sysInfo;
     56    FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser;
     57 
     58    GetSystemInfo(&sysInfo);
     59    assert(sysInfo.dwNumberOfProcessors >= 1);
     60    if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) {
     61       /* Tell hud_get_num_cpus there are only this many CPUs. */
     62       return FALSE;
     63    }
     64 
     65    /* Get accumulated user and sys time for all threads */
     66    if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit,
     67                         &ftKernel, &ftUser))
     68       return FALSE;
     69 
     70    GetSystemTimeAsFileTime(&ftNow);
     71 
     72    *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel);
     73    *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation);
     74 
     75    /* busy_time already has the time accross all cpus.
     76     * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the
     77     * following line.
     78     */
     79    *total_time *= sysInfo.dwNumberOfProcessors;
     80 
     81    /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage
     82     * and the system usage are one and the same.
     83     */
     84    return TRUE;
     85 }
     86 
     87 #else
     88 
     89 static boolean
     90 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
     91 {
     92    char cpuname[32];
     93    char line[1024];
     94    FILE *f;
     95 
     96    if (cpu_index == ALL_CPUS)
     97       strcpy(cpuname, "cpu");
     98    else
     99       sprintf(cpuname, "cpu%u", cpu_index);
    100 
    101    f = fopen("/proc/stat", "r");
    102    if (!f)
    103       return FALSE;
    104 
    105    while (!feof(f) && fgets(line, sizeof(line), f)) {
    106       if (strstr(line, cpuname) == line) {
    107          uint64_t v[12];
    108          int i, num;
    109 
    110          num = sscanf(line,
    111                       "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
    112                       " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
    113                       " %"PRIu64" %"PRIu64"",
    114                       cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5],
    115                       &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]);
    116          if (num < 5) {
    117             fclose(f);
    118             return FALSE;
    119          }
    120 
    121          /* user + nice + system */
    122          *busy_time = v[0] + v[1] + v[2];
    123          *total_time = *busy_time;
    124 
    125          /* ... + idle + iowait + irq + softirq + ...  */
    126          for (i = 3; i < num-1; i++) {
    127             *total_time += v[i];
    128          }
    129          fclose(f);
    130          return TRUE;
    131       }
    132    }
    133    fclose(f);
    134    return FALSE;
    135 }
    136 #endif
    137 
    138 
    139 struct cpu_info {
    140    unsigned cpu_index;
    141    uint64_t last_cpu_busy, last_cpu_total, last_time;
    142 };
    143 
    144 static void
    145 query_cpu_load(struct hud_graph *gr)
    146 {
    147    struct cpu_info *info = gr->query_data;
    148    uint64_t now = os_time_get();
    149 
    150    if (info->last_time) {
    151       if (info->last_time + gr->pane->period <= now) {
    152          uint64_t cpu_busy, cpu_total, cpu_load;
    153 
    154          get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total);
    155 
    156          cpu_load = (cpu_busy - info->last_cpu_busy) * 100 /
    157                     (double)(cpu_total - info->last_cpu_total);
    158          hud_graph_add_value(gr, cpu_load);
    159 
    160          info->last_cpu_busy = cpu_busy;
    161          info->last_cpu_total = cpu_total;
    162          info->last_time = now;
    163       }
    164    }
    165    else {
    166       /* initialize */
    167       info->last_time = now;
    168       get_cpu_stats(info->cpu_index, &info->last_cpu_busy,
    169                     &info->last_cpu_total);
    170    }
    171 }
    172 
    173 static void
    174 free_query_data(void *p)
    175 {
    176    FREE(p);
    177 }
    178 
    179 void
    180 hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index)
    181 {
    182    struct hud_graph *gr;
    183    struct cpu_info *info;
    184    uint64_t busy, total;
    185 
    186    /* see if the cpu exists */
    187    if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) {
    188       return;
    189    }
    190 
    191    gr = CALLOC_STRUCT(hud_graph);
    192    if (!gr)
    193       return;
    194 
    195    if (cpu_index == ALL_CPUS)
    196       strcpy(gr->name, "cpu");
    197    else
    198       sprintf(gr->name, "cpu%u", cpu_index);
    199 
    200    gr->query_data = CALLOC_STRUCT(cpu_info);
    201    if (!gr->query_data) {
    202       FREE(gr);
    203       return;
    204    }
    205 
    206    gr->query_new_value = query_cpu_load;
    207 
    208    /* Don't use free() as our callback as that messes up Gallium's
    209     * memory debugger.  Use simple free_query_data() wrapper.
    210     */
    211    gr->free_query_data = free_query_data;
    212 
    213    info = gr->query_data;
    214    info->cpu_index = cpu_index;
    215 
    216    hud_graph_set_dump_file(gr);
    217 
    218    hud_pane_add_graph(pane, gr);
    219    hud_pane_set_max_value(pane, 100);
    220 }
    221 
    222 int
    223 hud_get_num_cpus(void)
    224 {
    225    uint64_t busy, total;
    226    int i = 0;
    227 
    228    while (get_cpu_stats(i, &busy, &total))
    229       i++;
    230 
    231    return i;
    232 }
    233