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 "util/os_time.h"
     33 #include "os/os_thread.h"
     34 #include "util/u_memory.h"
     35 #include "util/u_queue.h"
     36 #include <stdio.h>
     37 #include <inttypes.h>
     38 #ifdef PIPE_OS_WINDOWS
     39 #include <windows.h>
     40 #endif
     41 
     42 
     43 #ifdef PIPE_OS_WINDOWS
     44 
     45 static inline uint64_t
     46 filetime_to_scalar(FILETIME ft)
     47 {
     48    ULARGE_INTEGER uli;
     49    uli.LowPart = ft.dwLowDateTime;
     50    uli.HighPart = ft.dwHighDateTime;
     51    return uli.QuadPart;
     52 }
     53 
     54 static boolean
     55 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
     56 {
     57    SYSTEM_INFO sysInfo;
     58    FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser;
     59 
     60    GetSystemInfo(&sysInfo);
     61    assert(sysInfo.dwNumberOfProcessors >= 1);
     62    if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) {
     63       /* Tell hud_get_num_cpus there are only this many CPUs. */
     64       return FALSE;
     65    }
     66 
     67    /* Get accumulated user and sys time for all threads */
     68    if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit,
     69                         &ftKernel, &ftUser))
     70       return FALSE;
     71 
     72    GetSystemTimeAsFileTime(&ftNow);
     73 
     74    *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel);
     75    *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation);
     76 
     77    /* busy_time already has the time accross all cpus.
     78     * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the
     79     * following line.
     80     */
     81    *total_time *= sysInfo.dwNumberOfProcessors;
     82 
     83    /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage
     84     * and the system usage are one and the same.
     85     */
     86    return TRUE;
     87 }
     88 
     89 #else
     90 
     91 static boolean
     92 get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
     93 {
     94    char cpuname[32];
     95    char line[1024];
     96    FILE *f;
     97 
     98    if (cpu_index == ALL_CPUS)
     99       strcpy(cpuname, "cpu");
    100    else
    101       sprintf(cpuname, "cpu%u", cpu_index);
    102 
    103    f = fopen("/proc/stat", "r");
    104    if (!f)
    105       return FALSE;
    106 
    107    while (!feof(f) && fgets(line, sizeof(line), f)) {
    108       if (strstr(line, cpuname) == line) {
    109          uint64_t v[12];
    110          int i, num;
    111 
    112          num = sscanf(line,
    113                       "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
    114                       " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
    115                       " %"PRIu64" %"PRIu64"",
    116                       cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5],
    117                       &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]);
    118          if (num < 5) {
    119             fclose(f);
    120             return FALSE;
    121          }
    122 
    123          /* user + nice + system */
    124          *busy_time = v[0] + v[1] + v[2];
    125          *total_time = *busy_time;
    126 
    127          /* ... + idle + iowait + irq + softirq + ...  */
    128          for (i = 3; i < num-1; i++) {
    129             *total_time += v[i];
    130          }
    131          fclose(f);
    132          return TRUE;
    133       }
    134    }
    135    fclose(f);
    136    return FALSE;
    137 }
    138 #endif
    139 
    140 
    141 struct cpu_info {
    142    unsigned cpu_index;
    143    uint64_t last_cpu_busy, last_cpu_total, last_time;
    144 };
    145 
    146 static void
    147 query_cpu_load(struct hud_graph *gr, struct pipe_context *pipe)
    148 {
    149    struct cpu_info *info = gr->query_data;
    150    uint64_t now = os_time_get();
    151 
    152    if (info->last_time) {
    153       if (info->last_time + gr->pane->period <= now) {
    154          uint64_t cpu_busy, cpu_total;
    155          double cpu_load;
    156 
    157          get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total);
    158 
    159          cpu_load = (cpu_busy - info->last_cpu_busy) * 100 /
    160                     (double)(cpu_total - info->last_cpu_total);
    161          hud_graph_add_value(gr, cpu_load);
    162 
    163          info->last_cpu_busy = cpu_busy;
    164          info->last_cpu_total = cpu_total;
    165          info->last_time = now;
    166       }
    167    }
    168    else {
    169       /* initialize */
    170       info->last_time = now;
    171       get_cpu_stats(info->cpu_index, &info->last_cpu_busy,
    172                     &info->last_cpu_total);
    173    }
    174 }
    175 
    176 static void
    177 free_query_data(void *p, struct pipe_context *pipe)
    178 {
    179    FREE(p);
    180 }
    181 
    182 void
    183 hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index)
    184 {
    185    struct hud_graph *gr;
    186    struct cpu_info *info;
    187    uint64_t busy, total;
    188 
    189    /* see if the cpu exists */
    190    if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) {
    191       return;
    192    }
    193 
    194    gr = CALLOC_STRUCT(hud_graph);
    195    if (!gr)
    196       return;
    197 
    198    if (cpu_index == ALL_CPUS)
    199       strcpy(gr->name, "cpu");
    200    else
    201       sprintf(gr->name, "cpu%u", cpu_index);
    202 
    203    gr->query_data = CALLOC_STRUCT(cpu_info);
    204    if (!gr->query_data) {
    205       FREE(gr);
    206       return;
    207    }
    208 
    209    gr->query_new_value = query_cpu_load;
    210 
    211    /* Don't use free() as our callback as that messes up Gallium's
    212     * memory debugger.  Use simple free_query_data() wrapper.
    213     */
    214    gr->free_query_data = free_query_data;
    215 
    216    info = gr->query_data;
    217    info->cpu_index = cpu_index;
    218 
    219    hud_pane_add_graph(pane, gr);
    220    hud_pane_set_max_value(pane, 100);
    221 }
    222 
    223 int
    224 hud_get_num_cpus(void)
    225 {
    226    uint64_t busy, total;
    227    int i = 0;
    228 
    229    while (get_cpu_stats(i, &busy, &total))
    230       i++;
    231 
    232    return i;
    233 }
    234 
    235 struct thread_info {
    236    bool main_thread;
    237    int64_t last_time;
    238    int64_t last_thread_time;
    239 };
    240 
    241 static void
    242 query_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe)
    243 {
    244    struct thread_info *info = gr->query_data;
    245    int64_t now = os_time_get_nano();
    246 
    247    if (info->last_time) {
    248       if (info->last_time + gr->pane->period*1000 <= now) {
    249          int64_t thread_now;
    250 
    251          if (info->main_thread) {
    252             thread_now = pipe_current_thread_get_time_nano();
    253          } else {
    254             struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
    255 
    256             if (mon && mon->queue)
    257                thread_now = util_queue_get_thread_time_nano(mon->queue, 0);
    258             else
    259                thread_now = 0;
    260          }
    261 
    262          double percent = (thread_now - info->last_thread_time) * 100.0 /
    263                             (now - info->last_time);
    264 
    265          /* Check if the context changed a thread, so that we don't show
    266           * a random value. When a thread is changed, the new thread clock
    267           * is different, which can result in "percent" being very high.
    268           */
    269          if (percent > 100.0)
    270             percent = 0.0;
    271          hud_graph_add_value(gr, percent);
    272 
    273          info->last_thread_time = thread_now;
    274          info->last_time = now;
    275       }
    276    } else {
    277       /* initialize */
    278       info->last_time = now;
    279       info->last_thread_time = pipe_current_thread_get_time_nano();
    280    }
    281 }
    282 
    283 void
    284 hud_thread_busy_install(struct hud_pane *pane, const char *name, bool main)
    285 {
    286    struct hud_graph *gr;
    287 
    288    gr = CALLOC_STRUCT(hud_graph);
    289    if (!gr)
    290       return;
    291 
    292    strcpy(gr->name, name);
    293 
    294    gr->query_data = CALLOC_STRUCT(thread_info);
    295    if (!gr->query_data) {
    296       FREE(gr);
    297       return;
    298    }
    299 
    300    ((struct thread_info*)gr->query_data)->main_thread = main;
    301    gr->query_new_value = query_api_thread_busy_status;
    302 
    303    /* Don't use free() as our callback as that messes up Gallium's
    304     * memory debugger.  Use simple free_query_data() wrapper.
    305     */
    306    gr->free_query_data = free_query_data;
    307 
    308    hud_pane_add_graph(pane, gr);
    309    hud_pane_set_max_value(pane, 100);
    310 }
    311 
    312 struct counter_info {
    313    enum hud_counter counter;
    314    unsigned last_value;
    315    int64_t last_time;
    316 };
    317 
    318 static unsigned get_counter(struct hud_graph *gr, enum hud_counter counter)
    319 {
    320    struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
    321 
    322    if (!mon || !mon->queue)
    323       return 0;
    324 
    325    switch (counter) {
    326    case HUD_COUNTER_OFFLOADED:
    327       return mon->num_offloaded_items;
    328    case HUD_COUNTER_DIRECT:
    329       return mon->num_direct_items;
    330    case HUD_COUNTER_SYNCS:
    331       return mon->num_syncs;
    332    default:
    333       assert(0);
    334       return 0;
    335    }
    336 }
    337 
    338 static void
    339 query_thread_counter(struct hud_graph *gr, struct pipe_context *pipe)
    340 {
    341    struct counter_info *info = gr->query_data;
    342    int64_t now = os_time_get_nano();
    343 
    344    if (info->last_time) {
    345       if (info->last_time + gr->pane->period*1000 <= now) {
    346          unsigned current_value = get_counter(gr, info->counter);
    347 
    348          hud_graph_add_value(gr, current_value - info->last_value);
    349          info->last_value = current_value;
    350          info->last_time = now;
    351       }
    352    } else {
    353       /* initialize */
    354       info->last_value = get_counter(gr, info->counter);
    355       info->last_time = now;
    356    }
    357 }
    358 
    359 void hud_thread_counter_install(struct hud_pane *pane, const char *name,
    360                                 enum hud_counter counter)
    361 {
    362    struct hud_graph *gr = CALLOC_STRUCT(hud_graph);
    363    if (!gr)
    364       return;
    365 
    366    strcpy(gr->name, name);
    367 
    368    gr->query_data = CALLOC_STRUCT(counter_info);
    369    if (!gr->query_data) {
    370       FREE(gr);
    371       return;
    372    }
    373 
    374    ((struct counter_info*)gr->query_data)->counter = counter;
    375    gr->query_new_value = query_thread_counter;
    376 
    377    /* Don't use free() as our callback as that messes up Gallium's
    378     * memory debugger.  Use simple free_query_data() wrapper.
    379     */
    380    gr->free_query_data = free_query_data;
    381 
    382    hud_pane_add_graph(pane, gr);
    383    hud_pane_set_max_value(pane, 100);
    384 }
    385