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 "os/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
)
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
, cpu_load
;
156 get_cpu_stats(info
->cpu_index
, &cpu_busy
, &cpu_total
);
158 cpu_load
= (cpu_busy
- info
->last_cpu_busy
) * 100 /
159 (double)(cpu_total
- info
->last_cpu_total
);
160 hud_graph_add_value(gr
, cpu_load
);
162 info
->last_cpu_busy
= cpu_busy
;
163 info
->last_cpu_total
= cpu_total
;
164 info
->last_time
= now
;
169 info
->last_time
= now
;
170 get_cpu_stats(info
->cpu_index
, &info
->last_cpu_busy
,
171 &info
->last_cpu_total
);
176 free_query_data(void *p
)
182 hud_cpu_graph_install(struct hud_pane
*pane
, unsigned cpu_index
)
184 struct hud_graph
*gr
;
185 struct cpu_info
*info
;
186 uint64_t busy
, total
;
188 /* see if the cpu exists */
189 if (cpu_index
!= ALL_CPUS
&& !get_cpu_stats(cpu_index
, &busy
, &total
)) {
193 gr
= CALLOC_STRUCT(hud_graph
);
197 if (cpu_index
== ALL_CPUS
)
198 strcpy(gr
->name
, "cpu");
200 sprintf(gr
->name
, "cpu%u", cpu_index
);
202 gr
->query_data
= CALLOC_STRUCT(cpu_info
);
203 if (!gr
->query_data
) {
208 gr
->query_new_value
= query_cpu_load
;
210 /* Don't use free() as our callback as that messes up Gallium's
211 * memory debugger. Use simple free_query_data() wrapper.
213 gr
->free_query_data
= free_query_data
;
215 info
= gr
->query_data
;
216 info
->cpu_index
= cpu_index
;
218 hud_pane_add_graph(pane
, gr
);
219 hud_pane_set_max_value(pane
, 100);
223 hud_get_num_cpus(void)
225 uint64_t busy
, total
;
228 while (get_cpu_stats(i
, &busy
, &total
))
237 int64_t last_thread_time
;
241 query_api_thread_busy_status(struct hud_graph
*gr
)
243 struct thread_info
*info
= gr
->query_data
;
244 int64_t now
= os_time_get_nano();
246 if (info
->last_time
) {
247 if (info
->last_time
+ gr
->pane
->period
*1000 <= now
) {
250 if (info
->main_thread
) {
251 thread_now
= pipe_current_thread_get_time_nano();
253 struct util_queue_monitoring
*mon
= gr
->pane
->hud
->monitored_queue
;
255 if (mon
&& mon
->queue
)
256 thread_now
= util_queue_get_thread_time_nano(mon
->queue
, 0);
261 unsigned percent
= (thread_now
- info
->last_thread_time
) * 100 /
262 (now
- info
->last_time
);
264 /* Check if the context changed a thread, so that we don't show
265 * a random value. When a thread is changed, the new thread clock
266 * is different, which can result in "percent" being very high.
270 hud_graph_add_value(gr
, percent
);
272 info
->last_thread_time
= thread_now
;
273 info
->last_time
= now
;
277 info
->last_time
= now
;
278 info
->last_thread_time
= pipe_current_thread_get_time_nano();
283 hud_thread_busy_install(struct hud_pane
*pane
, const char *name
, bool main
)
285 struct hud_graph
*gr
;
287 gr
= CALLOC_STRUCT(hud_graph
);
291 strcpy(gr
->name
, name
);
293 gr
->query_data
= CALLOC_STRUCT(thread_info
);
294 if (!gr
->query_data
) {
299 ((struct thread_info
*)gr
->query_data
)->main_thread
= main
;
300 gr
->query_new_value
= query_api_thread_busy_status
;
302 /* Don't use free() as our callback as that messes up Gallium's
303 * memory debugger. Use simple free_query_data() wrapper.
305 gr
->free_query_data
= free_query_data
;
307 hud_pane_add_graph(pane
, gr
);
308 hud_pane_set_max_value(pane
, 100);