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