Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/auxiliary/hud/hud_cpu.c
4565 views
1
/**************************************************************************
2
*
3
* Copyright 2013 Marek Olšák <[email protected]>
4
* All Rights Reserved.
5
*
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:
13
*
14
* The above copyright notice and this permission notice (including the
15
* next paragraph) shall be included in all copies or substantial portions
16
* of the Software.
17
*
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.
25
*
26
**************************************************************************/
27
28
/* This file contains code for reading CPU load for displaying on the HUD.
29
*/
30
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"
36
#include <stdio.h>
37
#include <inttypes.h>
38
#ifdef PIPE_OS_WINDOWS
39
#include <windows.h>
40
#endif
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>
46
#else
47
#include <sys/resource.h>
48
#endif
49
#endif
50
51
52
#ifdef PIPE_OS_WINDOWS
53
54
static inline uint64_t
55
filetime_to_scalar(FILETIME ft)
56
{
57
ULARGE_INTEGER uli;
58
uli.LowPart = ft.dwLowDateTime;
59
uli.HighPart = ft.dwHighDateTime;
60
return uli.QuadPart;
61
}
62
63
static boolean
64
get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
65
{
66
SYSTEM_INFO sysInfo;
67
FILETIME ftNow, ftCreation, ftExit, ftKernel, ftUser;
68
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. */
73
return FALSE;
74
}
75
76
/* Get accumulated user and sys time for all threads */
77
if (!GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit,
78
&ftKernel, &ftUser))
79
return FALSE;
80
81
GetSystemTimeAsFileTime(&ftNow);
82
83
*busy_time = filetime_to_scalar(ftUser) + filetime_to_scalar(ftKernel);
84
*total_time = filetime_to_scalar(ftNow) - filetime_to_scalar(ftCreation);
85
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
88
* following line.
89
*/
90
*total_time *= sysInfo.dwNumberOfProcessors;
91
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.
94
*/
95
return TRUE;
96
}
97
98
#elif defined(PIPE_OS_BSD)
99
100
static boolean
101
get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
102
{
103
#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD)
104
uint64_t cp_time[CPUSTATES];
105
#else
106
long cp_time[CPUSTATES];
107
#endif
108
size_t len;
109
110
if (cpu_index == ALL_CPUS) {
111
len = sizeof(cp_time);
112
113
#if defined(PIPE_OS_NETBSD)
114
int mib[] = { CTL_KERN, KERN_CP_TIME };
115
116
if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
117
return FALSE;
118
#elif defined(PIPE_OS_OPENBSD)
119
int mib[] = { CTL_KERN, KERN_CPTIME };
120
long sum_cp_time[CPUSTATES];
121
122
len = sizeof(sum_cp_time);
123
if (sysctl(mib, ARRAY_SIZE(mib), sum_cp_time, &len, NULL, 0) == -1)
124
return FALSE;
125
126
for (int state = 0; state < CPUSTATES; state++)
127
cp_time[state] = sum_cp_time[state];
128
#else
129
if (sysctlbyname("kern.cp_time", cp_time, &len, NULL, 0) == -1)
130
return FALSE;
131
#endif
132
} else {
133
#if defined(PIPE_OS_NETBSD)
134
int mib[] = { CTL_KERN, KERN_CP_TIME, cpu_index };
135
136
len = sizeof(cp_time);
137
if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
138
return FALSE;
139
#elif defined(PIPE_OS_OPENBSD)
140
int mib[] = { CTL_KERN, KERN_CPTIME2, cpu_index };
141
142
len = sizeof(cp_time);
143
if (sysctl(mib, ARRAY_SIZE(mib), cp_time, &len, NULL, 0) == -1)
144
return FALSE;
145
#else
146
long *cp_times = NULL;
147
148
if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) == -1)
149
return FALSE;
150
151
if (len < (cpu_index + 1) * sizeof(cp_time))
152
return FALSE;
153
154
cp_times = malloc(len);
155
156
if (sysctlbyname("kern.cp_times", cp_times, &len, NULL, 0) == -1)
157
return FALSE;
158
159
memcpy(cp_time, cp_times + (cpu_index * CPUSTATES),
160
sizeof(cp_time));
161
free(cp_times);
162
#endif
163
}
164
165
*busy_time = cp_time[CP_USER] + cp_time[CP_NICE] +
166
cp_time[CP_SYS] + cp_time[CP_INTR];
167
168
*total_time = *busy_time + cp_time[CP_IDLE];
169
170
return TRUE;
171
}
172
173
#else
174
175
static boolean
176
get_cpu_stats(unsigned cpu_index, uint64_t *busy_time, uint64_t *total_time)
177
{
178
char cpuname[32];
179
char line[1024];
180
FILE *f;
181
182
if (cpu_index == ALL_CPUS)
183
strcpy(cpuname, "cpu");
184
else
185
sprintf(cpuname, "cpu%u", cpu_index);
186
187
f = fopen("/proc/stat", "r");
188
if (!f)
189
return FALSE;
190
191
while (!feof(f) && fgets(line, sizeof(line), f)) {
192
if (strstr(line, cpuname) == line) {
193
uint64_t v[12];
194
int i, num;
195
196
num = sscanf(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]);
202
if (num < 5) {
203
fclose(f);
204
return FALSE;
205
}
206
207
/* user + nice + system */
208
*busy_time = v[0] + v[1] + v[2];
209
*total_time = *busy_time;
210
211
/* ... + idle + iowait + irq + softirq + ... */
212
for (i = 3; i < num-1; i++) {
213
*total_time += v[i];
214
}
215
fclose(f);
216
return TRUE;
217
}
218
}
219
fclose(f);
220
return FALSE;
221
}
222
#endif
223
224
225
struct cpu_info {
226
unsigned cpu_index;
227
uint64_t last_cpu_busy, last_cpu_total, last_time;
228
};
229
230
static void
231
query_cpu_load(struct hud_graph *gr, struct pipe_context *pipe)
232
{
233
struct cpu_info *info = gr->query_data;
234
uint64_t now = os_time_get();
235
236
if (info->last_time) {
237
if (info->last_time + gr->pane->period <= now) {
238
uint64_t cpu_busy, cpu_total;
239
double cpu_load;
240
241
get_cpu_stats(info->cpu_index, &cpu_busy, &cpu_total);
242
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);
246
247
info->last_cpu_busy = cpu_busy;
248
info->last_cpu_total = cpu_total;
249
info->last_time = now;
250
}
251
}
252
else {
253
/* initialize */
254
info->last_time = now;
255
get_cpu_stats(info->cpu_index, &info->last_cpu_busy,
256
&info->last_cpu_total);
257
}
258
}
259
260
static void
261
free_query_data(void *p, struct pipe_context *pipe)
262
{
263
FREE(p);
264
}
265
266
void
267
hud_cpu_graph_install(struct hud_pane *pane, unsigned cpu_index)
268
{
269
struct hud_graph *gr;
270
struct cpu_info *info;
271
uint64_t busy, total;
272
273
/* see if the cpu exists */
274
if (cpu_index != ALL_CPUS && !get_cpu_stats(cpu_index, &busy, &total)) {
275
return;
276
}
277
278
gr = CALLOC_STRUCT(hud_graph);
279
if (!gr)
280
return;
281
282
if (cpu_index == ALL_CPUS)
283
strcpy(gr->name, "cpu");
284
else
285
sprintf(gr->name, "cpu%u", cpu_index);
286
287
gr->query_data = CALLOC_STRUCT(cpu_info);
288
if (!gr->query_data) {
289
FREE(gr);
290
return;
291
}
292
293
gr->query_new_value = query_cpu_load;
294
295
/* Don't use free() as our callback as that messes up Gallium's
296
* memory debugger. Use simple free_query_data() wrapper.
297
*/
298
gr->free_query_data = free_query_data;
299
300
info = gr->query_data;
301
info->cpu_index = cpu_index;
302
303
hud_pane_add_graph(pane, gr);
304
hud_pane_set_max_value(pane, 100);
305
}
306
307
int
308
hud_get_num_cpus(void)
309
{
310
uint64_t busy, total;
311
int i = 0;
312
313
while (get_cpu_stats(i, &busy, &total))
314
i++;
315
316
return i;
317
}
318
319
struct thread_info {
320
bool main_thread;
321
int64_t last_time;
322
int64_t last_thread_time;
323
};
324
325
static void
326
query_api_thread_busy_status(struct hud_graph *gr, struct pipe_context *pipe)
327
{
328
struct thread_info *info = gr->query_data;
329
int64_t now = os_time_get_nano();
330
331
if (info->last_time) {
332
if (info->last_time + gr->pane->period*1000 <= now) {
333
int64_t thread_now;
334
335
if (info->main_thread) {
336
thread_now = util_current_thread_get_time_nano();
337
} else {
338
struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
339
340
if (mon && mon->queue)
341
thread_now = util_queue_get_thread_time_nano(mon->queue, 0);
342
else
343
thread_now = 0;
344
}
345
346
double percent = (thread_now - info->last_thread_time) * 100.0 /
347
(now - info->last_time);
348
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.
352
*/
353
if (percent > 100.0)
354
percent = 0.0;
355
hud_graph_add_value(gr, percent);
356
357
info->last_thread_time = thread_now;
358
info->last_time = now;
359
}
360
} else {
361
/* initialize */
362
info->last_time = now;
363
info->last_thread_time = util_current_thread_get_time_nano();
364
}
365
}
366
367
void
368
hud_thread_busy_install(struct hud_pane *pane, const char *name, bool main)
369
{
370
struct hud_graph *gr;
371
372
gr = CALLOC_STRUCT(hud_graph);
373
if (!gr)
374
return;
375
376
strcpy(gr->name, name);
377
378
gr->query_data = CALLOC_STRUCT(thread_info);
379
if (!gr->query_data) {
380
FREE(gr);
381
return;
382
}
383
384
((struct thread_info*)gr->query_data)->main_thread = main;
385
gr->query_new_value = query_api_thread_busy_status;
386
387
/* Don't use free() as our callback as that messes up Gallium's
388
* memory debugger. Use simple free_query_data() wrapper.
389
*/
390
gr->free_query_data = free_query_data;
391
392
hud_pane_add_graph(pane, gr);
393
hud_pane_set_max_value(pane, 100);
394
}
395
396
struct counter_info {
397
enum hud_counter counter;
398
unsigned last_value;
399
int64_t last_time;
400
};
401
402
static unsigned get_counter(struct hud_graph *gr, enum hud_counter counter)
403
{
404
struct util_queue_monitoring *mon = gr->pane->hud->monitored_queue;
405
406
if (!mon || !mon->queue)
407
return 0;
408
409
switch (counter) {
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;
416
default:
417
assert(0);
418
return 0;
419
}
420
}
421
422
static void
423
query_thread_counter(struct hud_graph *gr, struct pipe_context *pipe)
424
{
425
struct counter_info *info = gr->query_data;
426
int64_t now = os_time_get_nano();
427
428
if (info->last_time) {
429
if (info->last_time + gr->pane->period*1000 <= now) {
430
unsigned current_value = get_counter(gr, info->counter);
431
432
hud_graph_add_value(gr, current_value - info->last_value);
433
info->last_value = current_value;
434
info->last_time = now;
435
}
436
} else {
437
/* initialize */
438
info->last_value = get_counter(gr, info->counter);
439
info->last_time = now;
440
}
441
}
442
443
void hud_thread_counter_install(struct hud_pane *pane, const char *name,
444
enum hud_counter counter)
445
{
446
struct hud_graph *gr = CALLOC_STRUCT(hud_graph);
447
if (!gr)
448
return;
449
450
strcpy(gr->name, name);
451
452
gr->query_data = CALLOC_STRUCT(counter_info);
453
if (!gr->query_data) {
454
FREE(gr);
455
return;
456
}
457
458
((struct counter_info*)gr->query_data)->counter = counter;
459
gr->query_new_value = query_thread_counter;
460
461
/* Don't use free() as our callback as that messes up Gallium's
462
* memory debugger. Use simple free_query_data() wrapper.
463
*/
464
gr->free_query_data = free_query_data;
465
466
hud_pane_add_graph(pane, gr);
467
hud_pane_set_max_value(pane, 100);
468
}
469
470