1af69d88dSmrg/**************************************************************************
2af69d88dSmrg *
3af69d88dSmrg * Copyright 2013 Marek Olšák <maraeo@gmail.com>
4af69d88dSmrg * All Rights Reserved.
5af69d88dSmrg *
6af69d88dSmrg * Permission is hereby granted, free of charge, to any person obtaining a
7af69d88dSmrg * copy of this software and associated documentation files (the
8af69d88dSmrg * "Software"), to deal in the Software without restriction, including
9af69d88dSmrg * without limitation the rights to use, copy, modify, merge, publish,
10af69d88dSmrg * distribute, sub license, and/or sell copies of the Software, and to
11af69d88dSmrg * permit persons to whom the Software is furnished to do so, subject to
12af69d88dSmrg * the following conditions:
13af69d88dSmrg *
14af69d88dSmrg * The above copyright notice and this permission notice (including the
15af69d88dSmrg * next paragraph) shall be included in all copies or substantial portions
16af69d88dSmrg * of the Software.
17af69d88dSmrg *
18af69d88dSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19af69d88dSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20af69d88dSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21af69d88dSmrg * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
22af69d88dSmrg * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23af69d88dSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24af69d88dSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25af69d88dSmrg *
26af69d88dSmrg **************************************************************************/
27af69d88dSmrg
28af69d88dSmrg/* This file contains code for reading CPU load for displaying on the HUD.
29af69d88dSmrg */
30af69d88dSmrg
31af69d88dSmrg#include "hud/hud_private.h"
3201e04c3fSmrg#include "util/os_time.h"
3301e04c3fSmrg#include "os/os_thread.h"
34af69d88dSmrg#include "util/u_memory.h"
3501e04c3fSmrg#include "util/u_queue.h"
36af69d88dSmrg#include <stdio.h>
37af69d88dSmrg#include <inttypes.h>
3801e04c3fSmrg#ifdef PIPE_OS_WINDOWS
3901e04c3fSmrg#include <windows.h>
4001e04c3fSmrg#endif
417ec681f3Smrg#if defined(PIPE_OS_BSD)
42361fc4cbSmaya#include <sys/types.h>
43361fc4cbSmaya#include <sys/sysctl.h>
447ec681f3Smrg#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)
457ec681f3Smrg#include <sys/sched.h>
467ec681f3Smrg#else
47361fc4cbSmaya#include <sys/resource.h>
48361fc4cbSmaya#endif
497ec681f3Smrg#endif
5001e04c3fSmrg
5101e04c3fSmrg
5201e04c3fSmrg#ifdef PIPE_OS_WINDOWS
5301e04c3fSmrg
5401e04c3fSmrgstatic inline uint64_t
5501e04c3fSmrgfiletime_to_scalar(FILETIME ft)
5601e04c3fSmrg{
5701e04c3fSmrg   ULARGE_INTEGER uli;
5801e04c3fSmrg   uli.LowPart = ft.dwLowDateTime;
5901e04c3fSmrg   uli.HighPart = ft.dwHighDateTime;
6001e04c3fSmrg   return uli.QuadPart;
6101e04c3fSmrg}
6201e04c3fSmrg
6301e04c3fSmrgstatic boolean
6401e04c3fSmrgget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
6501e04c3fSmrg{
6601e04c3fSmrg   SYSTEM_INFO sysInfo;
6701e04c3fSmrg   FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser;
6801e04c3fSmrg
6901e04c3fSmrg   GetSystemInfo(&sysInfo);
7001e04c3fSmrg   assert(sysInfo.dwNumberOfProcessors >= 1);
7101e04c3fSmrg   if (cpu_index != ALL_CPUS && cpu_index >= sysInfo.dwNumberOfProcessors) {
7201e04c3fSmrg      /* Tell hud_get_num_cpus there are only this many CPUs. */
7301e04c3fSmrg      return FALSE;
7401e04c3fSmrg   }
7501e04c3fSmrg
7601e04c3fSmrg   /* Get accumulated user and sys time for all threads */
7701e04c3fSmrg   if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit,
7801e04c3fSmrg                        &ftKernel, &ftUser))
7901e04c3fSmrg      return FALSE;
8001e04c3fSmrg
8101e04c3fSmrg   GetSystemTimeAsFileTime(&ftNow);
8201e04c3fSmrg
8301e04c3fSmrg   *busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel);
8401e04c3fSmrg   *total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation);
8501e04c3fSmrg
8601e04c3fSmrg   /* busy_time already has the time accross all cpus.
8701e04c3fSmrg    * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the
8801e04c3fSmrg    * following line.
8901e04c3fSmrg    */
9001e04c3fSmrg   *total_time *= sysInfo.dwNumberOfProcessors;
9101e04c3fSmrg
9201e04c3fSmrg   /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage
9301e04c3fSmrg    * and the system usage are one and the same.
9401e04c3fSmrg    */
9501e04c3fSmrg   return TRUE;
9601e04c3fSmrg}
9701e04c3fSmrg
987ec681f3Smrg#elif defined(PIPE_OS_BSD)
99361fc4cbSmaya
100361fc4cbSmayastatic boolean
101361fc4cbSmayaget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
102361fc4cbSmaya{
1037ec681f3Smrg#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)
1047ec681f3Smrg   uint64_t cp_time[CPUSTATES];
1057ec681f3Smrg#else
106361fc4cbSmaya   long cp_time[CPUSTATES];
1077ec681f3Smrg#endif
108361fc4cbSmaya   size_t len;
109361fc4cbSmaya
110361fc4cbSmaya   if (cpu_index == ALL_CPUS) {
111361fc4cbSmaya      len = sizeof(cp_time);
112361fc4cbSmaya
1137ec681f3Smrg#if defined(PIPE_OS_NETBSD)
1147ec681f3Smrg      int mib[] = { CTL_KERN, KERN_CP_TIME };
1157ec681f3Smrg
1167ec681f3Smrg      if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
1177ec681f3Smrg         return FALSE;
1187ec681f3Smrg#elif defined(PIPE_OS_OPENBSD)
1197ec681f3Smrg      int mib[] = { CTL_KERN, KERN_CPTIME };
1207ec681f3Smrg      long sum_cp_time[CPUSTATES];
1217ec681f3Smrg
1227ec681f3Smrg      len = sizeof(sum_cp_time);
1237ec681f3Smrg      if (sysctl(mib, ARRAY_SIZE(mib), sum_cp_time, &len, NULL, 0) == -1)
1247ec681f3Smrg         return FALSE;
1257ec681f3Smrg
1267ec681f3Smrg      for (int state = 0; state < CPUSTATES; state++)
1277ec681f3Smrg         cp_time[state] = sum_cp_time[state];
1287ec681f3Smrg#else
129361fc4cbSmaya      if (sysctlbyname("kern.cp_time", cp_time, &len, NULL, 0) == -1)
130361fc4cbSmaya         return FALSE;
1317ec681f3Smrg#endif
132361fc4cbSmaya   } else {
1337ec681f3Smrg#if defined(PIPE_OS_NETBSD)
1347ec681f3Smrg      int mib[] = { CTL_KERN, KERN_CP_TIME, cpu_index };
1357ec681f3Smrg
1367ec681f3Smrg      len = sizeof(cp_time);
1377ec681f3Smrg      if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
1387ec681f3Smrg         return FALSE;
1397ec681f3Smrg#elif defined(PIPE_OS_OPENBSD)
1407ec681f3Smrg      int mib[] = { CTL_KERN, KERN_CPTIME2, cpu_index };
1417ec681f3Smrg
1427ec681f3Smrg      len = sizeof(cp_time);
1437ec681f3Smrg      if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
1447ec681f3Smrg         return FALSE;
1457ec681f3Smrg#else
146361fc4cbSmaya      long *cp_times = NULL;
147361fc4cbSmaya
148361fc4cbSmaya      if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) == -1)
149361fc4cbSmaya         return FALSE;
150361fc4cbSmaya
151361fc4cbSmaya      if (len < (cpu_index + 1) * sizeof(cp_time))
152361fc4cbSmaya         return FALSE;
153361fc4cbSmaya
154361fc4cbSmaya      cp_times = malloc(len);
155361fc4cbSmaya
156361fc4cbSmaya      if (sysctlbyname("kern.cp_times", cp_times, &len, NULL, 0) == -1)
157361fc4cbSmaya         return FALSE;
158361fc4cbSmaya
159361fc4cbSmaya      memcpy(cp_time, cp_times + (cpu_index * CPUSTATES),
160361fc4cbSmaya            sizeof(cp_time));
161361fc4cbSmaya      free(cp_times);
1627ec681f3Smrg#endif
163361fc4cbSmaya   }
164361fc4cbSmaya
165361fc4cbSmaya   *busy_time = cp_time[CP_USER] + cp_time[CP_NICE] +
166361fc4cbSmaya      cp_time[CP_SYS] + cp_time[CP_INTR];
167361fc4cbSmaya
168361fc4cbSmaya   *total_time = *busy_time + cp_time[CP_IDLE];
169361fc4cbSmaya
170361fc4cbSmaya   return TRUE;
171361fc4cbSmaya}
172361fc4cbSmaya
17301e04c3fSmrg#else
174af69d88dSmrg
175af69d88dSmrgstatic boolean
176af69d88dSmrgget_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
177af69d88dSmrg{
178af69d88dSmrg   char cpuname[32];
179af69d88dSmrg   char line[1024];
180af69d88dSmrg   FILE *f;
181af69d88dSmrg
182af69d88dSmrg   if (cpu_index == ALL_CPUS)
183af69d88dSmrg      strcpy(cpuname, "cpu");
184af69d88dSmrg   else
185af69d88dSmrg      sprintf(cpuname, "cpu%u", cpu_index);
186af69d88dSmrg
187af69d88dSmrg   f = fopen("/proc/stat", "r");
188af69d88dSmrg   if (!f)
189af69d88dSmrg      return FALSE;
190af69d88dSmrg
191af69d88dSmrg   while (!feof(f) && fgets(line, sizeof(line), f)) {
192af69d88dSmrg      if (strstr(line, cpuname) == line) {
193af69d88dSmrg         uint64_t v[12];
194af69d88dSmrg         int i, num;
195af69d88dSmrg
196af69d88dSmrg         num = sscanf(line,
197af69d88dSmrg                      "%s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
198af69d88dSmrg                      " %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64
199af69d88dSmrg                      " %"PRIu64" %"PRIu64"",
200af69d88dSmrg                      cpuname, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5],
201af69d88dSmrg                      &v[6], &v[7], &v[8], &v[9], &v[10], &v[11]);
202af69d88dSmrg         if (num < 5) {
203af69d88dSmrg            fclose(f);
204af69d88dSmrg            return FALSE;
205af69d88dSmrg         }
206af69d88dSmrg
207af69d88dSmrg         /* user + nice + system */
208af69d88dSmrg         *busy_time = v[0] + v[1] + v[2];
209af69d88dSmrg         *total_time = *busy_time;
210af69d88dSmrg
211af69d88dSmrg         /* ... + idle + iowait + irq + softirq + ...  */
212af69d88dSmrg         for (i = 3; i < num-1; i++) {
213af69d88dSmrg            *total_time += v[i];
214af69d88dSmrg         }
215af69d88dSmrg         fclose(f);
216af69d88dSmrg         return TRUE;
217af69d88dSmrg      }
218af69d88dSmrg   }
219af69d88dSmrg   fclose(f);
220af69d88dSmrg   return FALSE;
221af69d88dSmrg}
22201e04c3fSmrg#endif
22301e04c3fSmrg
224af69d88dSmrg
225af69d88dSmrgstruct cpu_info {
226af69d88dSmrg   unsigned cpu_index;
227af69d88dSmrg   uint64_t last_cpu_busy, last_cpu_total, last_time;
228af69d88dSmrg};
229af69d88dSmrg
230af69d88dSmrgstatic void
23101e04c3fSmrgquery_cpu_load(struct hud_graph *gr, struct pipe_context *pipe)
232af69d88dSmrg{
233af69d88dSmrg   struct cpu_info *info = gr->query_data;
234af69d88dSmrg   uint64_t now = os_time_get();
235af69d88dSmrg
236af69d88dSmrg   if (info->last_time) {
237af69d88dSmrg      if (info->last_time + gr->pane->period <= now) {
23801e04c3fSmrg         uint64_t cpu_busy, cpu_total;
23901e04c3fSmrg         double cpu_load;
240af69d88dSmrg
241af69d88dSmrg         get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total);
242af69d88dSmrg
243af69d88dSmrg         cpu_load = (cpu_busy - info->last_cpu_busy) * 100 /
244af69d88dSmrg                    (double)(cpu_total - info->last_cpu_total);
245af69d88dSmrg         hud_graph_add_value(gr, cpu_load);
246af69d88dSmrg
247af69d88dSmrg         info->last_cpu_busy = cpu_busy;
248af69d88dSmrg         info->last_cpu_total = cpu_total;
249af69d88dSmrg         info->last_time = now;
250af69d88dSmrg      }
251af69d88dSmrg   }
252af69d88dSmrg   else {
253af69d88dSmrg      /* initialize */
254af69d88dSmrg      info->last_time = now;
255af69d88dSmrg      get_cpu_stats(info->cpu_index, &info->last_cpu_busy,
256af69d88dSmrg                    &info->last_cpu_total);
257af69d88dSmrg   }
258af69d88dSmrg}
259af69d88dSmrg
260af69d88dSmrgstatic void
26101e04c3fSmrgfree_query_data(void *p, struct pipe_context *pipe)
262af69d88dSmrg{
263af69d88dSmrg   FREE(p);
264af69d88dSmrg}
265af69d88dSmrg
266af69d88dSmrgvoid
267af69d88dSmrghud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index)
268af69d88dSmrg{
269af69d88dSmrg   struct hud_graph *gr;
270af69d88dSmrg   struct cpu_info *info;
271af69d88dSmrg   uint64_t busy, total;
272af69d88dSmrg
273af69d88dSmrg   /* see if the cpu exists */
274af69d88dSmrg   if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) {
275af69d88dSmrg      return;
276af69d88dSmrg   }
277af69d88dSmrg
278af69d88dSmrg   gr = CALLOC_STRUCT(hud_graph);
279af69d88dSmrg   if (!gr)
280af69d88dSmrg      return;
281af69d88dSmrg
282af69d88dSmrg   if (cpu_index == ALL_CPUS)
283af69d88dSmrg      strcpy(gr->name, "cpu");
284af69d88dSmrg   else
285af69d88dSmrg      sprintf(gr->name, "cpu%u", cpu_index);
286af69d88dSmrg
287af69d88dSmrg   gr->query_data = CALLOC_STRUCT(cpu_info);
288af69d88dSmrg   if (!gr->query_data) {
289af69d88dSmrg      FREE(gr);
290af69d88dSmrg      return;
291af69d88dSmrg   }
292af69d88dSmrg
293af69d88dSmrg   gr->query_new_value = query_cpu_load;
294af69d88dSmrg
295af69d88dSmrg   /* Don't use free() as our callback as that messes up Gallium's
296af69d88dSmrg    * memory debugger.  Use simple free_query_data() wrapper.
297af69d88dSmrg    */
298af69d88dSmrg   gr->free_query_data = free_query_data;
299af69d88dSmrg
300af69d88dSmrg   info = gr->query_data;
301af69d88dSmrg   info->cpu_index = cpu_index;
302af69d88dSmrg
303af69d88dSmrg   hud_pane_add_graph(pane, gr);
304af69d88dSmrg   hud_pane_set_max_value(pane, 100);
305af69d88dSmrg}
306af69d88dSmrg
307af69d88dSmrgint
308af69d88dSmrghud_get_num_cpus(void)
309af69d88dSmrg{
310af69d88dSmrg   uint64_t busy, total;
311af69d88dSmrg   int i = 0;
312af69d88dSmrg
313af69d88dSmrg   while (get_cpu_stats(i, &busy, &total))
314af69d88dSmrg      i++;
315af69d88dSmrg
316af69d88dSmrg   return i;
317af69d88dSmrg}
31801e04c3fSmrg
31901e04c3fSmrgstruct thread_info {
32001e04c3fSmrg   bool main_thread;
32101e04c3fSmrg   int64_t last_time;
32201e04c3fSmrg   int64_t last_thread_time;
32301e04c3fSmrg};
32401e04c3fSmrg
32501e04c3fSmrgstatic void
32601e04c3fSmrgquery_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe)
32701e04c3fSmrg{
32801e04c3fSmrg   struct thread_info *info = gr->query_data;
32901e04c3fSmrg   int64_t now = os_time_get_nano();
33001e04c3fSmrg
33101e04c3fSmrg   if (info->last_time) {
33201e04c3fSmrg      if (info->last_time + gr->pane->period*1000 <= now) {
33301e04c3fSmrg         int64_t thread_now;
33401e04c3fSmrg
33501e04c3fSmrg         if (info->main_thread) {
3367ec681f3Smrg            thread_now = util_current_thread_get_time_nano();
33701e04c3fSmrg         } else {
33801e04c3fSmrg            struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
33901e04c3fSmrg
34001e04c3fSmrg            if (mon && mon->queue)
34101e04c3fSmrg               thread_now = util_queue_get_thread_time_nano(mon->queue, 0);
34201e04c3fSmrg            else
34301e04c3fSmrg               thread_now = 0;
34401e04c3fSmrg         }
34501e04c3fSmrg
34601e04c3fSmrg         double percent = (thread_now - info->last_thread_time) * 100.0 /
34701e04c3fSmrg                            (now - info->last_time);
34801e04c3fSmrg
34901e04c3fSmrg         /* Check if the context changed a thread, so that we don't show
35001e04c3fSmrg          * a random value. When a thread is changed, the new thread clock
35101e04c3fSmrg          * is different, which can result in "percent" being very high.
35201e04c3fSmrg          */
35301e04c3fSmrg         if (percent > 100.0)
35401e04c3fSmrg            percent = 0.0;
35501e04c3fSmrg         hud_graph_add_value(gr, percent);
35601e04c3fSmrg
35701e04c3fSmrg         info->last_thread_time = thread_now;
35801e04c3fSmrg         info->last_time = now;
35901e04c3fSmrg      }
36001e04c3fSmrg   } else {
36101e04c3fSmrg      /* initialize */
36201e04c3fSmrg      info->last_time = now;
3637ec681f3Smrg      info->last_thread_time = util_current_thread_get_time_nano();
36401e04c3fSmrg   }
36501e04c3fSmrg}
36601e04c3fSmrg
36701e04c3fSmrgvoid
36801e04c3fSmrghud_thread_busy_install(struct hud_pane *pane, const char *name, bool main)
36901e04c3fSmrg{
37001e04c3fSmrg   struct hud_graph *gr;
37101e04c3fSmrg
37201e04c3fSmrg   gr = CALLOC_STRUCT(hud_graph);
37301e04c3fSmrg   if (!gr)
37401e04c3fSmrg      return;
37501e04c3fSmrg
37601e04c3fSmrg   strcpy(gr->name, name);
37701e04c3fSmrg
37801e04c3fSmrg   gr->query_data = CALLOC_STRUCT(thread_info);
37901e04c3fSmrg   if (!gr->query_data) {
38001e04c3fSmrg      FREE(gr);
38101e04c3fSmrg      return;
38201e04c3fSmrg   }
38301e04c3fSmrg
38401e04c3fSmrg   ((struct thread_info*)gr->query_data)->main_thread = main;
38501e04c3fSmrg   gr->query_new_value = query_api_thread_busy_status;
38601e04c3fSmrg
38701e04c3fSmrg   /* Don't use free() as our callback as that messes up Gallium's
38801e04c3fSmrg    * memory debugger.  Use simple free_query_data() wrapper.
38901e04c3fSmrg    */
39001e04c3fSmrg   gr->free_query_data = free_query_data;
39101e04c3fSmrg
39201e04c3fSmrg   hud_pane_add_graph(pane, gr);
39301e04c3fSmrg   hud_pane_set_max_value(pane, 100);
39401e04c3fSmrg}
39501e04c3fSmrg
39601e04c3fSmrgstruct counter_info {
39701e04c3fSmrg   enum hud_counter counter;
39801e04c3fSmrg   unsigned last_value;
39901e04c3fSmrg   int64_t last_time;
40001e04c3fSmrg};
40101e04c3fSmrg
40201e04c3fSmrgstatic unsigned get_counter(struct hud_graph *gr, enum hud_counter counter)
40301e04c3fSmrg{
40401e04c3fSmrg   struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
40501e04c3fSmrg
40601e04c3fSmrg   if (!mon || !mon->queue)
40701e04c3fSmrg      return 0;
40801e04c3fSmrg
40901e04c3fSmrg   switch (counter) {
41001e04c3fSmrg   case HUD_COUNTER_OFFLOADED:
41101e04c3fSmrg      return mon->num_offloaded_items;
41201e04c3fSmrg   case HUD_COUNTER_DIRECT:
41301e04c3fSmrg      return mon->num_direct_items;
41401e04c3fSmrg   case HUD_COUNTER_SYNCS:
41501e04c3fSmrg      return mon->num_syncs;
41601e04c3fSmrg   default:
41701e04c3fSmrg      assert(0);
41801e04c3fSmrg      return 0;
41901e04c3fSmrg   }
42001e04c3fSmrg}
42101e04c3fSmrg
42201e04c3fSmrgstatic void
42301e04c3fSmrgquery_thread_counter(struct hud_graph *gr, struct pipe_context *pipe)
42401e04c3fSmrg{
42501e04c3fSmrg   struct counter_info *info = gr->query_data;
42601e04c3fSmrg   int64_t now = os_time_get_nano();
42701e04c3fSmrg
42801e04c3fSmrg   if (info->last_time) {
42901e04c3fSmrg      if (info->last_time + gr->pane->period*1000 <= now) {
43001e04c3fSmrg         unsigned current_value = get_counter(gr, info->counter);
43101e04c3fSmrg
43201e04c3fSmrg         hud_graph_add_value(gr, current_value - info->last_value);
43301e04c3fSmrg         info->last_value = current_value;
43401e04c3fSmrg         info->last_time = now;
43501e04c3fSmrg      }
43601e04c3fSmrg   } else {
43701e04c3fSmrg      /* initialize */
43801e04c3fSmrg      info->last_value = get_counter(gr, info->counter);
43901e04c3fSmrg      info->last_time = now;
44001e04c3fSmrg   }
44101e04c3fSmrg}
44201e04c3fSmrg
44301e04c3fSmrgvoid hud_thread_counter_install(struct hud_pane *pane, const char *name,
44401e04c3fSmrg                                enum hud_counter counter)
44501e04c3fSmrg{
44601e04c3fSmrg   struct hud_graph *gr = CALLOC_STRUCT(hud_graph);
44701e04c3fSmrg   if (!gr)
44801e04c3fSmrg      return;
44901e04c3fSmrg
45001e04c3fSmrg   strcpy(gr->name, name);
45101e04c3fSmrg
45201e04c3fSmrg   gr->query_data = CALLOC_STRUCT(counter_info);
45301e04c3fSmrg   if (!gr->query_data) {
45401e04c3fSmrg      FREE(gr);
45501e04c3fSmrg      return;
45601e04c3fSmrg   }
45701e04c3fSmrg
45801e04c3fSmrg   ((struct counter_info*)gr->query_data)->counter = counter;
45901e04c3fSmrg   gr->query_new_value = query_thread_counter;
46001e04c3fSmrg
46101e04c3fSmrg   /* Don't use free() as our callback as that messes up Gallium's
46201e04c3fSmrg    * memory debugger.  Use simple free_query_data() wrapper.
46301e04c3fSmrg    */
46401e04c3fSmrg   gr->free_query_data = free_query_data;
46501e04c3fSmrg
46601e04c3fSmrg   hud_pane_add_graph(pane, gr);
46701e04c3fSmrg   hud_pane_set_max_value(pane, 100);
46801e04c3fSmrg}
469