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
41 #if defined(PIPE_OS_BSD)
42 #include <sys/types.h>
43 #include <sys/sysctl.h>
44 #if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)
45 #include <sys/sched.h>
47 #include <sys/resource.h>
52 #ifdef PIPE_OS_WINDOWS
54 static inline uint64_t
55 filetime_to_scalar(FILETIME ft
)
58 uli
.LowPart
= ft
.dwLowDateTime
;
59 uli
.HighPart
= ft
.dwHighDateTime
;
64 get_cpu_stats(unsigned cpu_index
, uint64_t *busy_time
, uint64_t *total_time
)
67 FILETIME ftNow
, ftCreation
, ftExit
, ftKernel
, ftUser
;
69 GetSystemInfo(&sysInfo
);
70 assert(sysInfo
.dwNumberOfProcessors
>= 1);
71 if (cpu_index
!= ALL_CPUS
&& cpu_index
>= sysInfo
.dwNumberOfProcessors
) {
72 /* Tell hud_get_num_cpus there are only this many CPUs. */
76 /* Get accumulated user and sys time for all threads */
77 if (!GetProcessTimes(GetCurrentProcess(), &ftCreation
, &ftExit
,
81 GetSystemTimeAsFileTime(&ftNow
);
83 *busy_time
= filetime_to_scalar(ftUser
) + filetime_to_scalar(ftKernel
);
84 *total_time
= filetime_to_scalar(ftNow
) - filetime_to_scalar(ftCreation
);
86 /* busy_time already has the time accross all cpus.
87 * XXX: if we want 100% to mean one CPU, 200% two cpus, eliminate the
90 *total_time
*= sysInfo
.dwNumberOfProcessors
;
92 /* XXX: we ignore cpu_index, i.e, we assume that the individual CPU usage
93 * and the system usage are one and the same.
98 #elif defined(PIPE_OS_BSD)
101 get_cpu_stats(unsigned cpu_index
, uint64_t *busy_time
, uint64_t *total_time
)
103 #if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)
104 uint64_t cp_time
[CPUSTATES
];
106 long cp_time
[CPUSTATES
];
110 if (cpu_index
== ALL_CPUS
) {
111 len
= sizeof(cp_time
);
113 #if defined(PIPE_OS_NETBSD)
114 int mib
[] = { CTL_KERN
, KERN_CP_TIME
};
116 if (sysctl(mib
, ARRAY_SIZE(mib
), cp_time
, &len
, NULL
, 0) == -1)
118 #elif defined(PIPE_OS_OPENBSD)
119 int mib
[] = { CTL_KERN
, KERN_CPTIME
};
120 long sum_cp_time
[CPUSTATES
];
122 len
= sizeof(sum_cp_time
);
123 if (sysctl(mib
, ARRAY_SIZE(mib
), sum_cp_time
, &len
, NULL
, 0) == -1)
126 for (int state
= 0; state
< CPUSTATES
; state
++)
127 cp_time
[state
] = sum_cp_time
[state
];
129 if (sysctlbyname("kern.cp_time", cp_time
, &len
, NULL
, 0) == -1)
133 #if defined(PIPE_OS_NETBSD)
134 int mib
[] = { CTL_KERN
, KERN_CP_TIME
, cpu_index
};
136 len
= sizeof(cp_time
);
137 if (sysctl(mib
, ARRAY_SIZE(mib
), cp_time
, &len
, NULL
, 0) == -1)
139 #elif defined(PIPE_OS_OPENBSD)
140 int mib
[] = { CTL_KERN
, KERN_CPTIME2
, cpu_index
};
142 len
= sizeof(cp_time
);
143 if (sysctl(mib
, ARRAY_SIZE(mib
), cp_time
, &len
, NULL
, 0) == -1)
146 long *cp_times
= NULL
;
148 if (sysctlbyname("kern.cp_times", NULL
, &len
, NULL
, 0) == -1)
151 if (len
< (cpu_index
+ 1) * sizeof(cp_time
))
154 cp_times
= malloc(len
);
156 if (sysctlbyname("kern.cp_times", cp_times
, &len
, NULL
, 0) == -1)
159 memcpy(cp_time
, cp_times
+ (cpu_index
* CPUSTATES
),
165 *busy_time
= cp_time
[CP_USER
] + cp_time
[CP_NICE
] +
166 cp_time
[CP_SYS
] + cp_time
[CP_INTR
];
168 *total_time
= *busy_time
+ cp_time
[CP_IDLE
];
176 get_cpu_stats(unsigned cpu_index
, uint64_t *busy_time
, uint64_t *total_time
)
182 if (cpu_index
== ALL_CPUS
)
183 strcpy(cpuname
, "cpu");
185 sprintf(cpuname
, "cpu%u", cpu_index
);
187 f
= fopen("/proc/stat", "r");
191 while (!feof(f
) && fgets(line
, sizeof(line
), f
)) {
192 if (strstr(line
, cpuname
) == line
) {
197 "%s %"PRIu64
" %"PRIu64
" %"PRIu64
" %"PRIu64
" %"PRIu64
198 " %"PRIu64
" %"PRIu64
" %"PRIu64
" %"PRIu64
" %"PRIu64
199 " %"PRIu64
" %"PRIu64
"",
200 cpuname
, &v
[0], &v
[1], &v
[2], &v
[3], &v
[4], &v
[5],
201 &v
[6], &v
[7], &v
[8], &v
[9], &v
[10], &v
[11]);
207 /* user + nice + system */
208 *busy_time
= v
[0] + v
[1] + v
[2];
209 *total_time
= *busy_time
;
211 /* ... + idle + iowait + irq + softirq + ... */
212 for (i
= 3; i
< num
-1; i
++) {
227 uint64_t last_cpu_busy
, last_cpu_total
, last_time
;
231 query_cpu_load(struct hud_graph
*gr
, struct pipe_context
*pipe
)
233 struct cpu_info
*info
= gr
->query_data
;
234 uint64_t now
= os_time_get();
236 if (info
->last_time
) {
237 if (info
->last_time
+ gr
->pane
->period
<= now
) {
238 uint64_t cpu_busy
, cpu_total
;
241 get_cpu_stats(info
->cpu_index
, &cpu_busy
, &cpu_total
);
243 cpu_load
= (cpu_busy
- info
->last_cpu_busy
) * 100 /
244 (double)(cpu_total
- info
->last_cpu_total
);
245 hud_graph_add_value(gr
, cpu_load
);
247 info
->last_cpu_busy
= cpu_busy
;
248 info
->last_cpu_total
= cpu_total
;
249 info
->last_time
= now
;
254 info
->last_time
= now
;
255 get_cpu_stats(info
->cpu_index
, &info
->last_cpu_busy
,
256 &info
->last_cpu_total
);
261 free_query_data(void *p
, struct pipe_context
*pipe
)
267 hud_cpu_graph_install(struct hud_pane
*pane
, unsigned cpu_index
)
269 struct hud_graph
*gr
;
270 struct cpu_info
*info
;
271 uint64_t busy
, total
;
273 /* see if the cpu exists */
274 if (cpu_index
!= ALL_CPUS
&& !get_cpu_stats(cpu_index
, &busy
, &total
)) {
278 gr
= CALLOC_STRUCT(hud_graph
);
282 if (cpu_index
== ALL_CPUS
)
283 strcpy(gr
->name
, "cpu");
285 sprintf(gr
->name
, "cpu%u", cpu_index
);
287 gr
->query_data
= CALLOC_STRUCT(cpu_info
);
288 if (!gr
->query_data
) {
293 gr
->query_new_value
= query_cpu_load
;
295 /* Don't use free() as our callback as that messes up Gallium's
296 * memory debugger. Use simple free_query_data() wrapper.
298 gr
->free_query_data
= free_query_data
;
300 info
= gr
->query_data
;
301 info
->cpu_index
= cpu_index
;
303 hud_pane_add_graph(pane
, gr
);
304 hud_pane_set_max_value(pane
, 100);
308 hud_get_num_cpus(void)
310 uint64_t busy
, total
;
313 while (get_cpu_stats(i
, &busy
, &total
))
322 int64_t last_thread_time
;
326 query_api_thread_busy_status(struct hud_graph
*gr
, struct pipe_context
*pipe
)
328 struct thread_info
*info
= gr
->query_data
;
329 int64_t now
= os_time_get_nano();
331 if (info
->last_time
) {
332 if (info
->last_time
+ gr
->pane
->period
*1000 <= now
) {
335 if (info
->main_thread
) {
336 thread_now
= pipe_current_thread_get_time_nano();
338 struct util_queue_monitoring
*mon
= gr
->pane
->hud
->monitored_queue
;
340 if (mon
&& mon
->queue
)
341 thread_now
= util_queue_get_thread_time_nano(mon
->queue
, 0);
346 double percent
= (thread_now
- info
->last_thread_time
) * 100.0 /
347 (now
- info
->last_time
);
349 /* Check if the context changed a thread, so that we don't show
350 * a random value. When a thread is changed, the new thread clock
351 * is different, which can result in "percent" being very high.
355 hud_graph_add_value(gr
, percent
);
357 info
->last_thread_time
= thread_now
;
358 info
->last_time
= now
;
362 info
->last_time
= now
;
363 info
->last_thread_time
= pipe_current_thread_get_time_nano();
368 hud_thread_busy_install(struct hud_pane
*pane
, const char *name
, bool main
)
370 struct hud_graph
*gr
;
372 gr
= CALLOC_STRUCT(hud_graph
);
376 strcpy(gr
->name
, name
);
378 gr
->query_data
= CALLOC_STRUCT(thread_info
);
379 if (!gr
->query_data
) {
384 ((struct thread_info
*)gr
->query_data
)->main_thread
= main
;
385 gr
->query_new_value
= query_api_thread_busy_status
;
387 /* Don't use free() as our callback as that messes up Gallium's
388 * memory debugger. Use simple free_query_data() wrapper.
390 gr
->free_query_data
= free_query_data
;
392 hud_pane_add_graph(pane
, gr
);
393 hud_pane_set_max_value(pane
, 100);
396 struct counter_info
{
397 enum hud_counter counter
;
402 static unsigned get_counter(struct hud_graph
*gr
, enum hud_counter counter
)
404 struct util_queue_monitoring
*mon
= gr
->pane
->hud
->monitored_queue
;
406 if (!mon
|| !mon
->queue
)
410 case HUD_COUNTER_OFFLOADED
:
411 return mon
->num_offloaded_items
;
412 case HUD_COUNTER_DIRECT
:
413 return mon
->num_direct_items
;
414 case HUD_COUNTER_SYNCS
:
415 return mon
->num_syncs
;
423 query_thread_counter(struct hud_graph
*gr
, struct pipe_context
*pipe
)
425 struct counter_info
*info
= gr
->query_data
;
426 int64_t now
= os_time_get_nano();
428 if (info
->last_time
) {
429 if (info
->last_time
+ gr
->pane
->period
*1000 <= now
) {
430 unsigned current_value
= get_counter(gr
, info
->counter
);
432 hud_graph_add_value(gr
, current_value
- info
->last_value
);
433 info
->last_value
= current_value
;
434 info
->last_time
= now
;
438 info
->last_value
= get_counter(gr
, info
->counter
);
439 info
->last_time
= now
;
443 void hud_thread_counter_install(struct hud_pane
*pane
, const char *name
,
444 enum hud_counter counter
)
446 struct hud_graph
*gr
= CALLOC_STRUCT(hud_graph
);
450 strcpy(gr
->name
, name
);
452 gr
->query_data
= CALLOC_STRUCT(counter_info
);
453 if (!gr
->query_data
) {
458 ((struct counter_info
*)gr
->query_data
)->counter
= counter
;
459 gr
->query_new_value
= query_thread_counter
;
461 /* Don't use free() as our callback as that messes up Gallium's
462 * memory debugger. Use simple free_query_data() wrapper.
464 gr
->free_query_data
= free_query_data
;
466 hud_pane_add_graph(pane
, gr
);
467 hud_pane_set_max_value(pane
, 100);