Path: blob/21.2-virgl/src/gallium/auxiliary/hud/hud_cpu.c
4565 views
/**************************************************************************1*2* Copyright 2013 Marek Olšák <[email protected]>3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS18* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.20* IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR21* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,22* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE23* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.24*25**************************************************************************/2627/* This file contains code for reading CPU load for displaying on the HUD.28*/2930#include "hud/hud_private.h"31#include "util/os_time.h"32#include "os/os_thread.h"33#include "util/u_memory.h"34#include "util/u_queue.h"35#include <stdio.h>36#include <inttypes.h>37#ifdef PIPE_OS_WINDOWS38#include <windows.h>39#endif40#if defined(PIPE_OS_BSD)41#include <sys/types.h>42#include <sys/sysctl.h>43#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)44#include <sys/sched.h>45#else46#include <sys/resource.h>47#endif48#endif495051#ifdef PIPE_OS_WINDOWS5253static inline uint64_t54filetime_to_scalar(FILETIME ft)55{56ULARGE_INTEGER uli;57uli.LowPart = ft.dwLowDateTime;58uli.HighPart = ft.dwHighDateTime;59return uli.QuadPart;60}6162static boolean63get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)64{65SYSTEM_INFO sysInfo;66FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser;6768GetSystemInfo(&sysInfo);69assert(sysInfo.dwNumberOfProcessors >= 1);70if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) {71/* Tell hud_get_num_cpus there are only this many CPUs. */72return FALSE;73}7475/* Get accumulated user and sys time for all threads */76if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit,77&ftKernel, &ftUser))78return FALSE;7980GetSystemTimeAsFileTime(&ftNow);8182*busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel);83*total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation);8485/* busy_time already has the time accross all cpus.86* XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the87* following line.88*/89*total_time *= sysInfo.dwNumberOfProcessors;9091/* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage92* and the system usage are one and the same.93*/94return TRUE;95}9697#elif defined(PIPE_OS_BSD)9899static boolean100get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)101{102#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)103uint64_t cp_time[CPUSTATES];104#else105long cp_time[CPUSTATES];106#endif107size_t len;108109if (cpu_index == ALL_CPUS) {110len = sizeof(cp_time);111112#if defined(PIPE_OS_NETBSD)113int mib[] = { CTL_KERN, KERN_CP_TIME };114115if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)116return FALSE;117#elif defined(PIPE_OS_OPENBSD)118int mib[] = { CTL_KERN, KERN_CPTIME };119long sum_cp_time[CPUSTATES];120121len = sizeof(sum_cp_time);122if (sysctl(mib, ARRAY_SIZE(mib), sum_cp_time, &len, NULL, 0) == -1)123return FALSE;124125for (int state = 0; state < CPUSTATES; state++)126cp_time[state] = sum_cp_time[state];127#else128if (sysctlbyname("kern.cp_time", cp_time, &len, NULL, 0) == -1)129return FALSE;130#endif131} else {132#if defined(PIPE_OS_NETBSD)133int mib[] = { CTL_KERN, KERN_CP_TIME, cpu_index };134135len = sizeof(cp_time);136if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)137return FALSE;138#elif defined(PIPE_OS_OPENBSD)139int mib[] = { CTL_KERN, KERN_CPTIME2, cpu_index };140141len = sizeof(cp_time);142if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)143return FALSE;144#else145long *cp_times = NULL;146147if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) == -1)148return FALSE;149150if (len < (cpu_index + 1) * sizeof(cp_time))151return FALSE;152153cp_times = malloc(len);154155if (sysctlbyname("kern.cp_times", cp_times, &len, NULL, 0) == -1)156return FALSE;157158memcpy(cp_time, cp_times + (cpu_index * CPUSTATES),159sizeof(cp_time));160free(cp_times);161#endif162}163164*busy_time = cp_time[CP_USER] + cp_time[CP_NICE] +165cp_time[CP_SYS] + cp_time[CP_INTR];166167*total_time = *busy_time + cp_time[CP_IDLE];168169return TRUE;170}171172#else173174static boolean175get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)176{177char cpuname[32];178char line[1024];179FILE *f;180181if (cpu_index == ALL_CPUS)182strcpy(cpuname, "cpu");183else184sprintf(cpuname, "cpu%u", cpu_index);185186f = fopen("/proc/stat", "r");187if (!f)188return FALSE;189190while (!feof(f) && fgets(line, sizeof(line), f)) {191if (strstr(line, cpuname) == line) {192uint64_t v[12];193int i, num;194195num = sscanf(line,196"%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64197" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64198" %"PRIu64" %"PRIu64"",199cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5],200&v[6], &v[7], &v[8], &v[9], &v[10], &v[11]);201if (num < 5) {202fclose(f);203return FALSE;204}205206/* user + nice + system */207*busy_time = v[0] + v[1] + v[2];208*total_time = *busy_time;209210/* ... + idle + iowait + irq + softirq + ... */211for (i = 3; i < num-1; i++) {212*total_time += v[i];213}214fclose(f);215return TRUE;216}217}218fclose(f);219return FALSE;220}221#endif222223224struct cpu_info {225unsigned cpu_index;226uint64_t last_cpu_busy, last_cpu_total, last_time;227};228229static void230query_cpu_load(struct hud_graph *gr, struct pipe_context *pipe)231{232struct cpu_info *info = gr->query_data;233uint64_t now = os_time_get();234235if (info->last_time) {236if (info->last_time + gr->pane->period <= now) {237uint64_t cpu_busy, cpu_total;238double cpu_load;239240get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total);241242cpu_load = (cpu_busy - info->last_cpu_busy) * 100 /243(double)(cpu_total - info->last_cpu_total);244hud_graph_add_value(gr, cpu_load);245246info->last_cpu_busy = cpu_busy;247info->last_cpu_total = cpu_total;248info->last_time = now;249}250}251else {252/* initialize */253info->last_time = now;254get_cpu_stats(info->cpu_index, &info->last_cpu_busy,255&info->last_cpu_total);256}257}258259static void260free_query_data(void *p, struct pipe_context *pipe)261{262FREE(p);263}264265void266hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index)267{268struct hud_graph *gr;269struct cpu_info *info;270uint64_t busy, total;271272/* see if the cpu exists */273if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) {274return;275}276277gr = CALLOC_STRUCT(hud_graph);278if (!gr)279return;280281if (cpu_index == ALL_CPUS)282strcpy(gr->name, "cpu");283else284sprintf(gr->name, "cpu%u", cpu_index);285286gr->query_data = CALLOC_STRUCT(cpu_info);287if (!gr->query_data) {288FREE(gr);289return;290}291292gr->query_new_value = query_cpu_load;293294/* Don't use free() as our callback as that messes up Gallium's295* memory debugger. Use simple free_query_data() wrapper.296*/297gr->free_query_data = free_query_data;298299info = gr->query_data;300info->cpu_index = cpu_index;301302hud_pane_add_graph(pane, gr);303hud_pane_set_max_value(pane, 100);304}305306int307hud_get_num_cpus(void)308{309uint64_t busy, total;310int i = 0;311312while (get_cpu_stats(i, &busy, &total))313i++;314315return i;316}317318struct thread_info {319bool main_thread;320int64_t last_time;321int64_t last_thread_time;322};323324static void325query_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe)326{327struct thread_info *info = gr->query_data;328int64_t now = os_time_get_nano();329330if (info->last_time) {331if (info->last_time + gr->pane->period*1000 <= now) {332int64_t thread_now;333334if (info->main_thread) {335thread_now = util_current_thread_get_time_nano();336} else {337struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;338339if (mon && mon->queue)340thread_now = util_queue_get_thread_time_nano(mon->queue, 0);341else342thread_now = 0;343}344345double percent = (thread_now - info->last_thread_time) * 100.0 /346(now - info->last_time);347348/* Check if the context changed a thread, so that we don't show349* a random value. When a thread is changed, the new thread clock350* is different, which can result in "percent" being very high.351*/352if (percent > 100.0)353percent = 0.0;354hud_graph_add_value(gr, percent);355356info->last_thread_time = thread_now;357info->last_time = now;358}359} else {360/* initialize */361info->last_time = now;362info->last_thread_time = util_current_thread_get_time_nano();363}364}365366void367hud_thread_busy_install(struct hud_pane *pane, const char *name, bool main)368{369struct hud_graph *gr;370371gr = CALLOC_STRUCT(hud_graph);372if (!gr)373return;374375strcpy(gr->name, name);376377gr->query_data = CALLOC_STRUCT(thread_info);378if (!gr->query_data) {379FREE(gr);380return;381}382383((struct thread_info*)gr->query_data)->main_thread = main;384gr->query_new_value = query_api_thread_busy_status;385386/* Don't use free() as our callback as that messes up Gallium's387* memory debugger. Use simple free_query_data() wrapper.388*/389gr->free_query_data = free_query_data;390391hud_pane_add_graph(pane, gr);392hud_pane_set_max_value(pane, 100);393}394395struct counter_info {396enum hud_counter counter;397unsigned last_value;398int64_t last_time;399};400401static unsigned get_counter(struct hud_graph *gr, enum hud_counter counter)402{403struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;404405if (!mon || !mon->queue)406return 0;407408switch (counter) {409case HUD_COUNTER_OFFLOADED:410return mon->num_offloaded_items;411case HUD_COUNTER_DIRECT:412return mon->num_direct_items;413case HUD_COUNTER_SYNCS:414return mon->num_syncs;415default:416assert(0);417return 0;418}419}420421static void422query_thread_counter(struct hud_graph *gr, struct pipe_context *pipe)423{424struct counter_info *info = gr->query_data;425int64_t now = os_time_get_nano();426427if (info->last_time) {428if (info->last_time + gr->pane->period*1000 <= now) {429unsigned current_value = get_counter(gr, info->counter);430431hud_graph_add_value(gr, current_value - info->last_value);432info->last_value = current_value;433info->last_time = now;434}435} else {436/* initialize */437info->last_value = get_counter(gr, info->counter);438info->last_time = now;439}440}441442void hud_thread_counter_install(struct hud_pane *pane, const char *name,443enum hud_counter counter)444{445struct hud_graph *gr = CALLOC_STRUCT(hud_graph);446if (!gr)447return;448449strcpy(gr->name, name);450451gr->query_data = CALLOC_STRUCT(counter_info);452if (!gr->query_data) {453FREE(gr);454return;455}456457((struct counter_info*)gr->query_data)->counter = counter;458gr->query_new_value = query_thread_counter;459460/* Don't use free() as our callback as that messes up Gallium's461* memory debugger. Use simple free_query_data() wrapper.462*/463gr->free_query_data = free_query_data;464465hud_pane_add_graph(pane, gr);466hud_pane_set_max_value(pane, 100);467}468469470