1 /**************************************************************************
3 * Copyright 2013 Marek Olšák <maraeo@gmail.com>
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
28 /* This file contains code for reading CPU load for displaying on the HUD.
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"
38 #ifdef PIPE_OS_WINDOWS
43 #ifdef PIPE_OS_WINDOWS
45 static inline uint64_t
46 filetime_to_scalar(FILETIME ft
)
49 uli
.LowPart
= ft
.dwLowDateTime
;
50 uli
.HighPart
= ft
.dwHighDateTime
;
55 get_cpu_stats(unsigned cpu_index
, uint64_t *busy_time
, uint64_t *total_time
)
58 FILETIME ftNow
, ftCreation
, ftExit
, ftKernel
, ftUser
;
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. */
67 /* Get accumulated user and sys time for all threads */
68 if (!GetProcessTimes(GetCurrentProcess(), &ftCreation
, &ftExit
,
72 GetSystemTimeAsFileTime(&ftNow
);
74 *busy_time
= filetime_to_scalar(ftUser
) + filetime_to_scalar(ftKernel
);
75 *total_time
= filetime_to_scalar(ftNow
) - filetime_to_scalar(ftCreation
);
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
81 *total_time
*= sysInfo
.dwNumberOfProcessors
;
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.
92 get_cpu_stats(unsigned cpu_index
, uint64_t *busy_time
, uint64_t *total_time
)
98 if (cpu_index
== ALL_CPUS
)
99 strcpy(cpuname
, "cpu");
101 sprintf(cpuname
, "cpu%u", cpu_index
);
103 f
= fopen("/proc/stat", "r");
107 while (!feof(f
) && fgets(line
, sizeof(line
), f
)) {
108 if (strstr(line
, cpuname
) == 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]);
123 /* user + nice + system */
124 *busy_time
= v
[0] + v
[1] + v
[2];
125 *total_time
= *busy_time
;
127 /* ... + idle + iowait + irq + softirq + ... */
128 for (i
= 3; i
< num
-1; i
++) {
143 uint64_t last_cpu_busy
, last_cpu_total
, last_time
;
147 query_cpu_load(struct hud_graph
*gr
, struct pipe_context
*pipe
)
149 struct cpu_info
*info
= gr
->query_data
;
150 uint64_t now
= os_time_get();
152 if (info
->last_time
) {
153 if (info
->last_time
+ gr
->pane
->period
<= now
) {
154 uint64_t cpu_busy
, cpu_total
;
157 get_cpu_stats(info
->cpu_index
, &cpu_busy
, &cpu_total
);
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
);
163 info
->last_cpu_busy
= cpu_busy
;
164 info
->last_cpu_total
= cpu_total
;
165 info
->last_time
= now
;
170 info
->last_time
= now
;
171 get_cpu_stats(info
->cpu_index
, &info
->last_cpu_busy
,
172 &info
->last_cpu_total
);
177 free_query_data(void *p
, struct pipe_context
*pipe
)
183 hud_cpu_graph_install(struct hud_pane
*pane
, unsigned cpu_index
)
185 struct hud_graph
*gr
;
186 struct cpu_info
*info
;
187 uint64_t busy
, total
;
189 /* see if the cpu exists */
190 if (cpu_index
!= ALL_CPUS
&& !get_cpu_stats(cpu_index
, &busy
, &total
)) {
194 gr
= CALLOC_STRUCT(hud_graph
);
198 if (cpu_index
== ALL_CPUS
)
199 strcpy(gr
->name
, "cpu");
201 sprintf(gr
->name
, "cpu%u", cpu_index
);
203 gr
->query_data
= CALLOC_STRUCT(cpu_info
);
204 if (!gr
->query_data
) {
209 gr
->query_new_value
= query_cpu_load
;
211 /* Don't use free() as our callback as that messes up Gallium's
212 * memory debugger. Use simple free_query_data() wrapper.
214 gr
->free_query_data
= free_query_data
;
216 info
= gr
->query_data
;
217 info
->cpu_index
= cpu_index
;
219 hud_pane_add_graph(pane
, gr
);
220 hud_pane_set_max_value(pane
, 100);
224 hud_get_num_cpus(void)
226 uint64_t busy
, total
;
229 while (get_cpu_stats(i
, &busy
, &total
))
238 int64_t last_thread_time
;
242 query_api_thread_busy_status(struct hud_graph
*gr
, struct pipe_context
*pipe
)
244 struct thread_info
*info
= gr
->query_data
;
245 int64_t now
= os_time_get_nano();
247 if (info
->last_time
) {
248 if (info
->last_time
+ gr
->pane
->period
*1000 <= now
) {
251 if (info
->main_thread
) {
252 thread_now
= pipe_current_thread_get_time_nano();
254 struct util_queue_monitoring
*mon
= gr
->pane
->hud
->monitored_queue
;
256 if (mon
&& mon
->queue
)
257 thread_now
= util_queue_get_thread_time_nano(mon
->queue
, 0);
262 double percent
= (thread_now
- info
->last_thread_time
) * 100.0 /
263 (now
- info
->last_time
);
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.
271 hud_graph_add_value(gr
, percent
);
273 info
->last_thread_time
= thread_now
;
274 info
->last_time
= now
;
278 info
->last_time
= now
;
279 info
->last_thread_time
= pipe_current_thread_get_time_nano();
284 hud_thread_busy_install(struct hud_pane
*pane
, const char *name
, bool main
)
286 struct hud_graph
*gr
;
288 gr
= CALLOC_STRUCT(hud_graph
);
292 strcpy(gr
->name
, name
);
294 gr
->query_data
= CALLOC_STRUCT(thread_info
);
295 if (!gr
->query_data
) {
300 ((struct thread_info
*)gr
->query_data
)->main_thread
= main
;
301 gr
->query_new_value
= query_api_thread_busy_status
;
303 /* Don't use free() as our callback as that messes up Gallium's
304 * memory debugger. Use simple free_query_data() wrapper.
306 gr
->free_query_data
= free_query_data
;
308 hud_pane_add_graph(pane
, gr
);
309 hud_pane_set_max_value(pane
, 100);
312 struct counter_info
{
313 enum hud_counter counter
;
318 static unsigned get_counter(struct hud_graph
*gr
, enum hud_counter counter
)
320 struct util_queue_monitoring
*mon
= gr
->pane
->hud
->monitored_queue
;
322 if (!mon
|| !mon
->queue
)
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
;
339 query_thread_counter(struct hud_graph
*gr
, struct pipe_context
*pipe
)
341 struct counter_info
*info
= gr
->query_data
;
342 int64_t now
= os_time_get_nano();
344 if (info
->last_time
) {
345 if (info
->last_time
+ gr
->pane
->period
*1000 <= now
) {
346 unsigned current_value
= get_counter(gr
, info
->counter
);
348 hud_graph_add_value(gr
, current_value
- info
->last_value
);
349 info
->last_value
= current_value
;
350 info
->last_time
= now
;
354 info
->last_value
= get_counter(gr
, info
->counter
);
355 info
->last_time
= now
;
359 void hud_thread_counter_install(struct hud_pane
*pane
, const char *name
,
360 enum hud_counter counter
)
362 struct hud_graph
*gr
= CALLOC_STRUCT(hud_graph
);
366 strcpy(gr
->name
, name
);
368 gr
->query_data
= CALLOC_STRUCT(counter_info
);
369 if (!gr
->query_data
) {
374 ((struct counter_info
*)gr
->query_data
)->counter
= counter
;
375 gr
->query_new_value
= query_thread_counter
;
377 /* Don't use free() as our callback as that messes up Gallium's
378 * memory debugger. Use simple free_query_data() wrapper.
380 gr
->free_query_data
= free_query_data
;
382 hud_pane_add_graph(pane
, gr
);
383 hud_pane_set_max_value(pane
, 100);