1848b8605Smrg/************************************************************************** 2848b8605Smrg * 3848b8605Smrg * Copyright 2013 Marek Olšák <maraeo@gmail.com> 4848b8605Smrg * All Rights Reserved. 5848b8605Smrg * 6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a 7848b8605Smrg * copy of this software and associated documentation files (the 8848b8605Smrg * "Software"), to deal in the Software without restriction, including 9848b8605Smrg * without limitation the rights to use, copy, modify, merge, publish, 10848b8605Smrg * distribute, sub license, and/or sell copies of the Software, and to 11848b8605Smrg * permit persons to whom the Software is furnished to do so, subject to 12848b8605Smrg * the following conditions: 13848b8605Smrg * 14848b8605Smrg * The above copyright notice and this permission notice (including the 15848b8605Smrg * next paragraph) shall be included in all copies or substantial portions 16848b8605Smrg * of the Software. 17848b8605Smrg * 18848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20848b8605Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21848b8605Smrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 22848b8605Smrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23848b8605Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24848b8605Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25848b8605Smrg * 26848b8605Smrg **************************************************************************/ 27848b8605Smrg 28848b8605Smrg/* This file contains code for reading CPU load for displaying on the HUD. 29848b8605Smrg */ 30848b8605Smrg 31848b8605Smrg#include "hud/hud_private.h" 32b8e80941Smrg#include "util/os_time.h" 33b8e80941Smrg#include "os/os_thread.h" 34848b8605Smrg#include "util/u_memory.h" 35b8e80941Smrg#include "util/u_queue.h" 36848b8605Smrg#include <stdio.h> 37848b8605Smrg#include <inttypes.h> 38b8e80941Smrg#ifdef PIPE_OS_WINDOWS 39b8e80941Smrg#include <windows.h> 40b8e80941Smrg#endif 41b8e80941Smrg#ifdef PIPE_OS_FREEBSD 42b8e80941Smrg#include <sys/types.h> 43b8e80941Smrg#include <sys/sysctl.h> 44b8e80941Smrg#include <sys/resource.h> 45b8e80941Smrg#endif 46b8e80941Smrg 47b8e80941Smrg 48b8e80941Smrg#ifdef PIPE_OS_WINDOWS 49b8e80941Smrg 50b8e80941Smrgstatic inline uint64_t 51b8e80941Smrgfiletime_to_scalar(FILETIME ft) 52b8e80941Smrg{ 53b8e80941Smrg ULARGE_INTEGER uli; 54b8e80941Smrg uli.LowPart = ft.dwLowDateTime; 55b8e80941Smrg uli.HighPart = ft.dwHighDateTime; 56b8e80941Smrg return uli.QuadPart; 57b8e80941Smrg} 58b8e80941Smrg 59b8e80941Smrgstatic boolean 60b8e80941Smrgget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 61b8e80941Smrg{ 62b8e80941Smrg SYSTEM_INFO sysInfo; 63b8e80941Smrg FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser; 64b8e80941Smrg 65b8e80941Smrg GetSystemInfo(&sysInfo); 66b8e80941Smrg assert(sysInfo.dwNumberOfProcessors >= 1); 67b8e80941Smrg if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) { 68b8e80941Smrg /* Tell hud_get_num_cpus there are only this many CPUs. */ 69b8e80941Smrg return FALSE; 70b8e80941Smrg } 71b8e80941Smrg 72b8e80941Smrg /* Get accumulated user and sys time for all threads */ 73b8e80941Smrg if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, 74b8e80941Smrg &ftKernel, &ftUser)) 75b8e80941Smrg return FALSE; 76b8e80941Smrg 77b8e80941Smrg GetSystemTimeAsFileTime(&ftNow); 78b8e80941Smrg 79b8e80941Smrg *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel); 80b8e80941Smrg *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation); 81b8e80941Smrg 82b8e80941Smrg /* busy_time already has the time accross all cpus. 83b8e80941Smrg * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the 84b8e80941Smrg * following line. 85b8e80941Smrg */ 86b8e80941Smrg *total_time *= sysInfo.dwNumberOfProcessors; 87b8e80941Smrg 88b8e80941Smrg /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage 89b8e80941Smrg * and the system usage are one and the same. 90b8e80941Smrg */ 91b8e80941Smrg return TRUE; 92b8e80941Smrg} 93b8e80941Smrg 94b8e80941Smrg#elif defined(PIPE_OS_FREEBSD) 95b8e80941Smrg 96b8e80941Smrgstatic boolean 97b8e80941Smrgget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 98b8e80941Smrg{ 99b8e80941Smrg long cp_time[CPUSTATES]; 100b8e80941Smrg size_t len; 101b8e80941Smrg 102b8e80941Smrg if (cpu_index == ALL_CPUS) { 103b8e80941Smrg len = sizeof(cp_time); 104b8e80941Smrg 105b8e80941Smrg if (sysctlbyname("kern.cp_time", cp_time, &len, NULL, 0) == -1) 106b8e80941Smrg return FALSE; 107b8e80941Smrg } else { 108b8e80941Smrg long *cp_times = NULL; 109b8e80941Smrg 110b8e80941Smrg if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) == -1) 111b8e80941Smrg return FALSE; 112b8e80941Smrg 113b8e80941Smrg if (len < (cpu_index + 1) * sizeof(cp_time)) 114b8e80941Smrg return FALSE; 115b8e80941Smrg 116b8e80941Smrg cp_times = malloc(len); 117b8e80941Smrg 118b8e80941Smrg if (sysctlbyname("kern.cp_times", cp_times, &len, NULL, 0) == -1) 119b8e80941Smrg return FALSE; 120b8e80941Smrg 121b8e80941Smrg memcpy(cp_time, cp_times + (cpu_index * CPUSTATES), 122b8e80941Smrg sizeof(cp_time)); 123b8e80941Smrg free(cp_times); 124b8e80941Smrg } 125b8e80941Smrg 126b8e80941Smrg *busy_time = cp_time[CP_USER] + cp_time[CP_NICE] + 127b8e80941Smrg cp_time[CP_SYS] + cp_time[CP_INTR]; 128b8e80941Smrg 129b8e80941Smrg *total_time = *busy_time + cp_time[CP_IDLE]; 130b8e80941Smrg 131b8e80941Smrg return TRUE; 132b8e80941Smrg} 133b8e80941Smrg 134b8e80941Smrg#else 135848b8605Smrg 136848b8605Smrgstatic boolean 137848b8605Smrgget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time) 138848b8605Smrg{ 139848b8605Smrg char cpuname[32]; 140848b8605Smrg char line[1024]; 141848b8605Smrg FILE *f; 142848b8605Smrg 143848b8605Smrg if (cpu_index == ALL_CPUS) 144848b8605Smrg strcpy(cpuname, "cpu"); 145848b8605Smrg else 146848b8605Smrg sprintf(cpuname, "cpu%u", cpu_index); 147848b8605Smrg 148848b8605Smrg f = fopen("/proc/stat", "r"); 149848b8605Smrg if (!f) 150848b8605Smrg return FALSE; 151848b8605Smrg 152848b8605Smrg while (!feof(f) && fgets(line, sizeof(line), f)) { 153848b8605Smrg if (strstr(line, cpuname) == line) { 154848b8605Smrg uint64_t v[12]; 155848b8605Smrg int i, num; 156848b8605Smrg 157848b8605Smrg num = sscanf(line, 158848b8605Smrg "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 159848b8605Smrg " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64 160848b8605Smrg " %"PRIu64" %"PRIu64"", 161848b8605Smrg cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], 162848b8605Smrg &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]); 163848b8605Smrg if (num < 5) { 164848b8605Smrg fclose(f); 165848b8605Smrg return FALSE; 166848b8605Smrg } 167848b8605Smrg 168848b8605Smrg /* user + nice + system */ 169848b8605Smrg *busy_time = v[0] + v[1] + v[2]; 170848b8605Smrg *total_time = *busy_time; 171848b8605Smrg 172848b8605Smrg /* ... + idle + iowait + irq + softirq + ... */ 173848b8605Smrg for (i = 3; i < num-1; i++) { 174848b8605Smrg *total_time += v[i]; 175848b8605Smrg } 176848b8605Smrg fclose(f); 177848b8605Smrg return TRUE; 178848b8605Smrg } 179848b8605Smrg } 180848b8605Smrg fclose(f); 181848b8605Smrg return FALSE; 182848b8605Smrg} 183b8e80941Smrg#endif 184b8e80941Smrg 185848b8605Smrg 186848b8605Smrgstruct cpu_info { 187848b8605Smrg unsigned cpu_index; 188848b8605Smrg uint64_t last_cpu_busy, last_cpu_total, last_time; 189848b8605Smrg}; 190848b8605Smrg 191848b8605Smrgstatic void 192b8e80941Smrgquery_cpu_load(struct hud_graph *gr, struct pipe_context *pipe) 193848b8605Smrg{ 194848b8605Smrg struct cpu_info *info = gr->query_data; 195848b8605Smrg uint64_t now = os_time_get(); 196848b8605Smrg 197848b8605Smrg if (info->last_time) { 198848b8605Smrg if (info->last_time + gr->pane->period <= now) { 199b8e80941Smrg uint64_t cpu_busy, cpu_total; 200b8e80941Smrg double cpu_load; 201848b8605Smrg 202848b8605Smrg get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total); 203848b8605Smrg 204848b8605Smrg cpu_load = (cpu_busy - info->last_cpu_busy) * 100 / 205848b8605Smrg (double)(cpu_total - info->last_cpu_total); 206848b8605Smrg hud_graph_add_value(gr, cpu_load); 207848b8605Smrg 208848b8605Smrg info->last_cpu_busy = cpu_busy; 209848b8605Smrg info->last_cpu_total = cpu_total; 210848b8605Smrg info->last_time = now; 211848b8605Smrg } 212848b8605Smrg } 213848b8605Smrg else { 214848b8605Smrg /* initialize */ 215848b8605Smrg info->last_time = now; 216848b8605Smrg get_cpu_stats(info->cpu_index, &info->last_cpu_busy, 217848b8605Smrg &info->last_cpu_total); 218848b8605Smrg } 219848b8605Smrg} 220848b8605Smrg 221848b8605Smrgstatic void 222b8e80941Smrgfree_query_data(void *p, struct pipe_context *pipe) 223848b8605Smrg{ 224848b8605Smrg FREE(p); 225848b8605Smrg} 226848b8605Smrg 227848b8605Smrgvoid 228848b8605Smrghud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index) 229848b8605Smrg{ 230848b8605Smrg struct hud_graph *gr; 231848b8605Smrg struct cpu_info *info; 232848b8605Smrg uint64_t busy, total; 233848b8605Smrg 234848b8605Smrg /* see if the cpu exists */ 235848b8605Smrg if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) { 236848b8605Smrg return; 237848b8605Smrg } 238848b8605Smrg 239848b8605Smrg gr = CALLOC_STRUCT(hud_graph); 240848b8605Smrg if (!gr) 241848b8605Smrg return; 242848b8605Smrg 243848b8605Smrg if (cpu_index == ALL_CPUS) 244848b8605Smrg strcpy(gr->name, "cpu"); 245848b8605Smrg else 246848b8605Smrg sprintf(gr->name, "cpu%u", cpu_index); 247848b8605Smrg 248848b8605Smrg gr->query_data = CALLOC_STRUCT(cpu_info); 249848b8605Smrg if (!gr->query_data) { 250848b8605Smrg FREE(gr); 251848b8605Smrg return; 252848b8605Smrg } 253848b8605Smrg 254848b8605Smrg gr->query_new_value = query_cpu_load; 255848b8605Smrg 256848b8605Smrg /* Don't use free() as our callback as that messes up Gallium's 257848b8605Smrg * memory debugger. Use simple free_query_data() wrapper. 258848b8605Smrg */ 259848b8605Smrg gr->free_query_data = free_query_data; 260848b8605Smrg 261848b8605Smrg info = gr->query_data; 262848b8605Smrg info->cpu_index = cpu_index; 263848b8605Smrg 264848b8605Smrg hud_pane_add_graph(pane, gr); 265848b8605Smrg hud_pane_set_max_value(pane, 100); 266848b8605Smrg} 267848b8605Smrg 268848b8605Smrgint 269848b8605Smrghud_get_num_cpus(void) 270848b8605Smrg{ 271848b8605Smrg uint64_t busy, total; 272848b8605Smrg int i = 0; 273848b8605Smrg 274848b8605Smrg while (get_cpu_stats(i, &busy, &total)) 275848b8605Smrg i++; 276848b8605Smrg 277848b8605Smrg return i; 278848b8605Smrg} 279b8e80941Smrg 280b8e80941Smrgstruct thread_info { 281b8e80941Smrg bool main_thread; 282b8e80941Smrg int64_t last_time; 283b8e80941Smrg int64_t last_thread_time; 284b8e80941Smrg}; 285b8e80941Smrg 286b8e80941Smrgstatic void 287b8e80941Smrgquery_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe) 288b8e80941Smrg{ 289b8e80941Smrg struct thread_info *info = gr->query_data; 290b8e80941Smrg int64_t now = os_time_get_nano(); 291b8e80941Smrg 292b8e80941Smrg if (info->last_time) { 293b8e80941Smrg if (info->last_time + gr->pane->period*1000 <= now) { 294b8e80941Smrg int64_t thread_now; 295b8e80941Smrg 296b8e80941Smrg if (info->main_thread) { 297b8e80941Smrg thread_now = pipe_current_thread_get_time_nano(); 298b8e80941Smrg } else { 299b8e80941Smrg struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue; 300b8e80941Smrg 301b8e80941Smrg if (mon && mon->queue) 302b8e80941Smrg thread_now = util_queue_get_thread_time_nano(mon->queue, 0); 303b8e80941Smrg else 304b8e80941Smrg thread_now = 0; 305b8e80941Smrg } 306b8e80941Smrg 307b8e80941Smrg double percent = (thread_now - info->last_thread_time) * 100.0 / 308b8e80941Smrg (now - info->last_time); 309b8e80941Smrg 310b8e80941Smrg /* Check if the context changed a thread, so that we don't show 311b8e80941Smrg * a random value. When a thread is changed, the new thread clock 312b8e80941Smrg * is different, which can result in "percent" being very high. 313b8e80941Smrg */ 314b8e80941Smrg if (percent > 100.0) 315b8e80941Smrg percent = 0.0; 316b8e80941Smrg hud_graph_add_value(gr, percent); 317b8e80941Smrg 318b8e80941Smrg info->last_thread_time = thread_now; 319b8e80941Smrg info->last_time = now; 320b8e80941Smrg } 321b8e80941Smrg } else { 322b8e80941Smrg /* initialize */ 323b8e80941Smrg info->last_time = now; 324b8e80941Smrg info->last_thread_time = pipe_current_thread_get_time_nano(); 325b8e80941Smrg } 326b8e80941Smrg} 327b8e80941Smrg 328b8e80941Smrgvoid 329b8e80941Smrghud_thread_busy_install(struct hud_pane *pane, const char *name, bool main) 330b8e80941Smrg{ 331b8e80941Smrg struct hud_graph *gr; 332b8e80941Smrg 333b8e80941Smrg gr = CALLOC_STRUCT(hud_graph); 334b8e80941Smrg if (!gr) 335b8e80941Smrg return; 336b8e80941Smrg 337b8e80941Smrg strcpy(gr->name, name); 338b8e80941Smrg 339b8e80941Smrg gr->query_data = CALLOC_STRUCT(thread_info); 340b8e80941Smrg if (!gr->query_data) { 341b8e80941Smrg FREE(gr); 342b8e80941Smrg return; 343b8e80941Smrg } 344b8e80941Smrg 345b8e80941Smrg ((struct thread_info*)gr->query_data)->main_thread = main; 346b8e80941Smrg gr->query_new_value = query_api_thread_busy_status; 347b8e80941Smrg 348b8e80941Smrg /* Don't use free() as our callback as that messes up Gallium's 349b8e80941Smrg * memory debugger. Use simple free_query_data() wrapper. 350b8e80941Smrg */ 351b8e80941Smrg gr->free_query_data = free_query_data; 352b8e80941Smrg 353b8e80941Smrg hud_pane_add_graph(pane, gr); 354b8e80941Smrg hud_pane_set_max_value(pane, 100); 355b8e80941Smrg} 356b8e80941Smrg 357b8e80941Smrgstruct counter_info { 358b8e80941Smrg enum hud_counter counter; 359b8e80941Smrg unsigned last_value; 360b8e80941Smrg int64_t last_time; 361b8e80941Smrg}; 362b8e80941Smrg 363b8e80941Smrgstatic unsigned get_counter(struct hud_graph *gr, enum hud_counter counter) 364b8e80941Smrg{ 365b8e80941Smrg struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue; 366b8e80941Smrg 367b8e80941Smrg if (!mon || !mon->queue) 368b8e80941Smrg return 0; 369b8e80941Smrg 370b8e80941Smrg switch (counter) { 371b8e80941Smrg case HUD_COUNTER_OFFLOADED: 372b8e80941Smrg return mon->num_offloaded_items; 373b8e80941Smrg case HUD_COUNTER_DIRECT: 374b8e80941Smrg return mon->num_direct_items; 375b8e80941Smrg case HUD_COUNTER_SYNCS: 376b8e80941Smrg return mon->num_syncs; 377b8e80941Smrg default: 378b8e80941Smrg assert(0); 379b8e80941Smrg return 0; 380b8e80941Smrg } 381b8e80941Smrg} 382b8e80941Smrg 383b8e80941Smrgstatic void 384b8e80941Smrgquery_thread_counter(struct hud_graph *gr, struct pipe_context *pipe) 385b8e80941Smrg{ 386b8e80941Smrg struct counter_info *info = gr->query_data; 387b8e80941Smrg int64_t now = os_time_get_nano(); 388b8e80941Smrg 389b8e80941Smrg if (info->last_time) { 390b8e80941Smrg if (info->last_time + gr->pane->period*1000 <= now) { 391b8e80941Smrg unsigned current_value = get_counter(gr, info->counter); 392b8e80941Smrg 393b8e80941Smrg hud_graph_add_value(gr, current_value - info->last_value); 394b8e80941Smrg info->last_value = current_value; 395b8e80941Smrg info->last_time = now; 396b8e80941Smrg } 397b8e80941Smrg } else { 398b8e80941Smrg /* initialize */ 399b8e80941Smrg info->last_value = get_counter(gr, info->counter); 400b8e80941Smrg info->last_time = now; 401b8e80941Smrg } 402b8e80941Smrg} 403b8e80941Smrg 404b8e80941Smrgvoid hud_thread_counter_install(struct hud_pane *pane, const char *name, 405b8e80941Smrg enum hud_counter counter) 406b8e80941Smrg{ 407b8e80941Smrg struct hud_graph *gr = CALLOC_STRUCT(hud_graph); 408b8e80941Smrg if (!gr) 409b8e80941Smrg return; 410b8e80941Smrg 411b8e80941Smrg strcpy(gr->name, name); 412b8e80941Smrg 413b8e80941Smrg gr->query_data = CALLOC_STRUCT(counter_info); 414b8e80941Smrg if (!gr->query_data) { 415b8e80941Smrg FREE(gr); 416b8e80941Smrg return; 417b8e80941Smrg } 418b8e80941Smrg 419b8e80941Smrg ((struct counter_info*)gr->query_data)->counter = counter; 420b8e80941Smrg gr->query_new_value = query_thread_counter; 421b8e80941Smrg 422b8e80941Smrg /* Don't use free() as our callback as that messes up Gallium's 423b8e80941Smrg * memory debugger. Use simple free_query_data() wrapper. 424b8e80941Smrg */ 425b8e80941Smrg gr->free_query_data = free_query_data; 426b8e80941Smrg 427b8e80941Smrg hud_pane_add_graph(pane, gr); 428b8e80941Smrg hud_pane_set_max_value(pane, 100); 429b8e80941Smrg} 430