Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/intel/tools/aubinator_viewer.cpp
4547 views
1
/*
2
* Copyright © 2016 Intel Corporation
3
*
4
* Permission is hereby granted, free of charge, to any person obtaining a
5
* copy of this software and associated documentation files (the "Software"),
6
* to deal in the Software without restriction, including without limitation
7
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
* and/or sell copies of the Software, and to permit persons to whom the
9
* Software is furnished to do so, subject to the following conditions:
10
*
11
* The above copyright notice and this permission notice (including the next
12
* paragraph) shall be included in all copies or substantial portions of the
13
* Software.
14
*
15
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
* IN THE SOFTWARE.
22
*/
23
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <stdint.h>
27
#include <getopt.h>
28
#include <unistd.h>
29
#include <fcntl.h>
30
#include <string.h>
31
#include <errno.h>
32
#include <sys/stat.h>
33
#include <sys/mman.h>
34
#include <sys/types.h>
35
#include <ctype.h>
36
37
#include "util/macros.h"
38
39
#include "aub_read.h"
40
#include "aub_mem.h"
41
42
#include "common/intel_disasm.h"
43
44
#define xtzalloc(name) ((decltype(&name)) calloc(1, sizeof(name)))
45
#define xtalloc(name) ((decltype(&name)) malloc(sizeof(name)))
46
47
struct aub_file {
48
uint8_t *map, *end, *cursor;
49
50
uint16_t pci_id;
51
char app_name[33];
52
53
/* List of batch buffers to process */
54
struct {
55
const uint8_t *start;
56
const uint8_t *end;
57
} *execs;
58
int n_execs;
59
int n_allocated_execs;
60
61
uint32_t idx_reg_write;
62
63
/* Device state */
64
struct intel_device_info devinfo;
65
struct intel_spec *spec;
66
};
67
68
static void
69
store_exec_begin(struct aub_file *file)
70
{
71
if (unlikely(file->n_execs >= file->n_allocated_execs)) {
72
file->n_allocated_execs = MAX2(2U * file->n_allocated_execs,
73
4096 / sizeof(file->execs[0]));
74
file->execs = (decltype(file->execs))
75
realloc(static_cast<void *>(file->execs),
76
file->n_allocated_execs * sizeof(file->execs[0]));
77
}
78
79
file->execs[file->n_execs++].start = file->cursor;
80
}
81
82
static void
83
store_exec_end(struct aub_file *file)
84
{
85
if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)
86
file->execs[file->n_execs - 1].end = file->cursor;
87
}
88
89
static void
90
handle_mem_write(void *user_data, uint64_t phys_addr,
91
const void *data, uint32_t data_len)
92
{
93
struct aub_file *file = (struct aub_file *) user_data;
94
file->idx_reg_write = 0;
95
store_exec_end(file);
96
}
97
98
static void
99
handle_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,
100
const void *ring_data, uint32_t ring_data_len)
101
{
102
struct aub_file *file = (struct aub_file *) user_data;
103
file->idx_reg_write = 0;
104
store_exec_begin(file);
105
}
106
107
static void
108
handle_reg_write(void *user_data, uint32_t reg_offset, uint32_t reg_value)
109
{
110
struct aub_file *file = (struct aub_file *) user_data;
111
112
/* Only store the first register write of a series (execlist writes take
113
* involve 2 dwords).
114
*/
115
if (file->idx_reg_write++ == 0)
116
store_exec_begin(file);
117
}
118
119
static void
120
handle_info(void *user_data, int pci_id, const char *app_name)
121
{
122
struct aub_file *file = (struct aub_file *) user_data;
123
store_exec_end(file);
124
125
file->pci_id = pci_id;
126
snprintf(file->app_name, sizeof(app_name), "%s", app_name);
127
128
if (!intel_get_device_info_from_pci_id(file->pci_id, &file->devinfo)) {
129
fprintf(stderr, "can't find device information: pci_id=0x%x\n", file->pci_id);
130
exit(EXIT_FAILURE);
131
}
132
file->spec = intel_spec_load(&file->devinfo);
133
}
134
135
static void
136
handle_error(void *user_data, const void *aub_data, const char *msg)
137
{
138
fprintf(stderr, "ERROR: %s", msg);
139
}
140
141
static struct aub_file *
142
aub_file_open(const char *filename)
143
{
144
struct aub_file *file;
145
struct stat sb;
146
int fd;
147
148
file = xtzalloc(*file);
149
fd = open(filename, O_RDWR);
150
if (fd == -1) {
151
fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno));
152
exit(EXIT_FAILURE);
153
}
154
155
if (fstat(fd, &sb) == -1) {
156
fprintf(stderr, "stat failed: %s\n", strerror(errno));
157
exit(EXIT_FAILURE);
158
}
159
160
file->map = (uint8_t *) mmap(NULL, sb.st_size,
161
PROT_READ, MAP_SHARED, fd, 0);
162
if (file->map == MAP_FAILED) {
163
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
164
exit(EXIT_FAILURE);
165
}
166
167
close(fd);
168
169
file->cursor = file->map;
170
file->end = file->map + sb.st_size;
171
172
struct aub_read aub_read = {};
173
aub_read.user_data = file;
174
aub_read.info = handle_info;
175
aub_read.error = handle_error;
176
aub_read.reg_write = handle_reg_write;
177
aub_read.ring_write = handle_ring_write;
178
aub_read.local_write = handle_mem_write;
179
aub_read.phys_write = handle_mem_write;
180
aub_read.ggtt_write = handle_mem_write;
181
aub_read.ggtt_entry_write = handle_mem_write;
182
183
int consumed;
184
while (file->cursor < file->end &&
185
(consumed = aub_read_command(&aub_read, file->cursor,
186
file->end - file->cursor)) > 0) {
187
file->cursor += consumed;
188
}
189
190
/* Ensure we have an end on the last register write. */
191
if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)
192
file->execs[file->n_execs - 1].end = file->end;
193
194
return file;
195
}
196
197
/**/
198
199
static void
200
update_mem_for_exec(struct aub_mem *mem, struct aub_file *file, int exec_idx)
201
{
202
struct aub_read read = {};
203
read.user_data = mem;
204
read.local_write = aub_mem_local_write;
205
read.phys_write = aub_mem_phys_write;
206
read.ggtt_write = aub_mem_ggtt_write;
207
read.ggtt_entry_write = aub_mem_ggtt_entry_write;
208
209
/* Replay the aub file from the beginning up to just before the
210
* commands we want to read. where the context setup happens.
211
*/
212
const uint8_t *iter = file->map;
213
while (iter < file->execs[exec_idx].start) {
214
iter += aub_read_command(&read, iter, file->execs[exec_idx].start - iter);
215
}
216
}
217
218
/* UI */
219
220
#include <epoxy/gl.h>
221
222
#include "imgui/imgui.h"
223
#include "imgui/imgui_memory_editor.h"
224
#include "imgui_impl_gtk3.h"
225
#include "imgui_impl_opengl3.h"
226
227
#include "aubinator_viewer.h"
228
#include "aubinator_viewer_urb.h"
229
230
struct window {
231
struct list_head link; /* link in the global list of windows */
232
struct list_head parent_link; /* link in parent window list of children */
233
234
struct list_head children_windows; /* list of children windows */
235
236
char name[128];
237
bool opened;
238
239
ImVec2 position;
240
ImVec2 size;
241
242
void (*display)(struct window*);
243
void (*destroy)(struct window*);
244
};
245
246
struct edit_window {
247
struct window base;
248
249
struct aub_mem *mem;
250
uint64_t address;
251
uint32_t len;
252
253
struct intel_batch_decode_bo aub_bo;
254
uint64_t aub_offset;
255
256
struct intel_batch_decode_bo gtt_bo;
257
uint64_t gtt_offset;
258
259
struct MemoryEditor editor;
260
};
261
262
struct pml4_window {
263
struct window base;
264
265
struct aub_mem *mem;
266
};
267
268
struct shader_window {
269
struct window base;
270
271
uint64_t address;
272
char *shader;
273
size_t shader_size;
274
};
275
276
struct urb_window {
277
struct window base;
278
279
uint32_t end_urb_offset;
280
struct aub_decode_urb_stage_state urb_stages[AUB_DECODE_N_STAGE];
281
282
AubinatorViewerUrb urb_view;
283
};
284
285
struct batch_window {
286
struct window base;
287
288
struct aub_mem mem;
289
struct aub_read read;
290
291
bool uses_ppgtt;
292
293
bool collapsed;
294
int exec_idx;
295
296
struct aub_viewer_decode_cfg decode_cfg;
297
struct aub_viewer_decode_ctx decode_ctx;
298
299
struct pml4_window pml4_window;
300
301
char edit_address[20];
302
};
303
304
static struct Context {
305
struct aub_file *file;
306
char *input_file;
307
char *xml_path;
308
309
GtkWidget *gtk_window;
310
311
/* UI state*/
312
bool show_commands_window;
313
bool show_registers_window;
314
315
struct aub_viewer_cfg cfg;
316
317
struct list_head windows;
318
319
struct window file_window;
320
struct window commands_window;
321
struct window registers_window;
322
} context;
323
324
thread_local ImGuiContext* __MesaImGui;
325
326
static int
327
map_key(int k)
328
{
329
return ImGuiKey_COUNT + k;
330
}
331
332
static bool
333
has_ctrl_key(int key)
334
{
335
return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));
336
}
337
338
static bool
339
window_has_ctrl_key(int key)
340
{
341
return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
342
}
343
344
static void
345
destroy_window_noop(struct window *win)
346
{
347
}
348
349
/* Shader windows */
350
351
static void
352
display_shader_window(struct window *win)
353
{
354
struct shader_window *window = (struct shader_window *) win;
355
356
if (window->shader) {
357
ImGui::InputTextMultiline("Assembly",
358
window->shader, window->shader_size,
359
ImGui::GetContentRegionAvail(),
360
ImGuiInputTextFlags_ReadOnly);
361
} else {
362
ImGui::Text("Shader not available");
363
}
364
}
365
366
static void
367
destroy_shader_window(struct window *win)
368
{
369
struct shader_window *window = (struct shader_window *) win;
370
371
free(window->shader);
372
free(window);
373
}
374
375
static struct shader_window *
376
new_shader_window(struct aub_mem *mem, uint64_t address, const char *desc)
377
{
378
struct shader_window *window = xtzalloc(*window);
379
380
snprintf(window->base.name, sizeof(window->base.name),
381
"%s (0x%" PRIx64 ")##%p", desc, address, window);
382
383
list_inithead(&window->base.parent_link);
384
window->base.position = ImVec2(-1, -1);
385
window->base.size = ImVec2(700, 300);
386
window->base.opened = true;
387
window->base.display = display_shader_window;
388
window->base.destroy = destroy_shader_window;
389
390
struct intel_batch_decode_bo shader_bo =
391
aub_mem_get_ppgtt_bo(mem, address);
392
if (shader_bo.map) {
393
FILE *f = open_memstream(&window->shader, &window->shader_size);
394
if (f) {
395
intel_disassemble(&context.file->devinfo,
396
(const uint8_t *) shader_bo.map +
397
(address - shader_bo.addr), 0, f);
398
fclose(f);
399
}
400
}
401
402
list_addtail(&window->base.link, &context.windows);
403
404
return window;
405
}
406
407
/* URB windows */
408
409
static void
410
display_urb_window(struct window *win)
411
{
412
struct urb_window *window = (struct urb_window *) win;
413
static const char *stages[] = {
414
[AUB_DECODE_STAGE_VS] = "VS",
415
[AUB_DECODE_STAGE_HS] = "HS",
416
[AUB_DECODE_STAGE_DS] = "DS",
417
[AUB_DECODE_STAGE_GS] = "GS",
418
[AUB_DECODE_STAGE_PS] = "PS",
419
[AUB_DECODE_STAGE_CS] = "CS",
420
};
421
422
ImGui::Text("URB allocation:");
423
window->urb_view.DrawAllocation("##urb",
424
ARRAY_SIZE(window->urb_stages),
425
window->end_urb_offset,
426
stages,
427
&window->urb_stages[0]);
428
}
429
430
static void
431
destroy_urb_window(struct window *win)
432
{
433
struct urb_window *window = (struct urb_window *) win;
434
435
free(window);
436
}
437
438
static struct urb_window *
439
new_urb_window(struct aub_viewer_decode_ctx *decode_ctx, uint64_t address)
440
{
441
struct urb_window *window = xtzalloc(*window);
442
443
snprintf(window->base.name, sizeof(window->base.name),
444
"URB view (0x%" PRIx64 ")##%p", address, window);
445
446
list_inithead(&window->base.parent_link);
447
window->base.position = ImVec2(-1, -1);
448
window->base.size = ImVec2(700, 300);
449
window->base.opened = true;
450
window->base.display = display_urb_window;
451
window->base.destroy = destroy_urb_window;
452
453
window->end_urb_offset = decode_ctx->end_urb_offset;
454
memcpy(window->urb_stages, decode_ctx->urb_stages, sizeof(window->urb_stages));
455
window->urb_view = AubinatorViewerUrb();
456
457
list_addtail(&window->base.link, &context.windows);
458
459
return window;
460
}
461
462
/* Memory editor windows */
463
464
static uint8_t
465
read_edit_window(const uint8_t *data, size_t off)
466
{
467
struct edit_window *window = (struct edit_window *) data;
468
469
return *((const uint8_t *) window->gtt_bo.map + window->gtt_offset + off);
470
}
471
472
static void
473
write_edit_window(uint8_t *data, size_t off, uint8_t d)
474
{
475
struct edit_window *window = (struct edit_window *) data;
476
uint8_t *gtt = (uint8_t *) window->gtt_bo.map + window->gtt_offset + off;
477
uint8_t *aub = (uint8_t *) window->aub_bo.map + window->aub_offset + off;
478
479
*gtt = *aub = d;
480
}
481
482
static void
483
display_edit_window(struct window *win)
484
{
485
struct edit_window *window = (struct edit_window *) win;
486
487
if (window->aub_bo.map && window->gtt_bo.map) {
488
ImGui::BeginChild(ImGui::GetID("##block"));
489
window->editor.DrawContents((uint8_t *) window,
490
MIN3(window->len,
491
window->gtt_bo.size - window->gtt_offset,
492
window->aub_bo.size - window->aub_offset),
493
window->address);
494
ImGui::EndChild();
495
} else {
496
ImGui::Text("Memory view at 0x%" PRIx64 " not available", window->address);
497
}
498
}
499
500
static void
501
destroy_edit_window(struct window *win)
502
{
503
struct edit_window *window = (struct edit_window *) win;
504
505
if (window->aub_bo.map)
506
mprotect((void *) window->aub_bo.map, 4096, PROT_READ);
507
free(window);
508
}
509
510
static struct edit_window *
511
new_edit_window(struct aub_mem *mem, uint64_t address, uint32_t len)
512
{
513
struct edit_window *window = xtzalloc(*window);
514
515
snprintf(window->base.name, sizeof(window->base.name),
516
"Editing aub at 0x%" PRIx64 "##%p", address, window);
517
518
list_inithead(&window->base.parent_link);
519
window->base.position = ImVec2(-1, -1);
520
window->base.size = ImVec2(500, 600);
521
window->base.opened = true;
522
window->base.display = display_edit_window;
523
window->base.destroy = destroy_edit_window;
524
525
window->mem = mem;
526
window->address = address;
527
window->aub_bo = aub_mem_get_ppgtt_addr_aub_data(mem, address);
528
window->gtt_bo = aub_mem_get_ppgtt_addr_data(mem, address);
529
window->len = len;
530
window->editor = MemoryEditor();
531
window->editor.OptShowDataPreview = true;
532
window->editor.OptShowAscii = false;
533
window->editor.ReadFn = read_edit_window;
534
window->editor.WriteFn = write_edit_window;
535
536
if (window->aub_bo.map) {
537
uint64_t unaligned_map = (uint64_t) window->aub_bo.map;
538
window->aub_bo.map = (const void *)(unaligned_map & ~0xffful);
539
window->aub_offset = unaligned_map - (uint64_t) window->aub_bo.map;
540
541
if (mprotect((void *) window->aub_bo.map, window->aub_bo.size, PROT_READ | PROT_WRITE) != 0) {
542
window->aub_bo.map = NULL;
543
}
544
}
545
546
window->gtt_offset = address - window->gtt_bo.addr;
547
548
list_addtail(&window->base.link, &context.windows);
549
550
return window;
551
}
552
553
/* 4 level page table walk windows */
554
555
static void
556
display_pml4_level(struct aub_mem *mem, uint64_t table_addr, uint64_t table_virt_addr, int level)
557
{
558
if (level == 0)
559
return;
560
561
struct intel_batch_decode_bo table_bo =
562
aub_mem_get_phys_addr_data(mem, table_addr);
563
const uint64_t *table = (const uint64_t *) ((const uint8_t *) table_bo.map +
564
table_addr - table_bo.addr);
565
if (!table) {
566
ImGui::TextColored(context.cfg.missing_color, "Page not available");
567
return;
568
}
569
570
uint64_t addr_increment = 1ULL << (12 + 9 * (level - 1));
571
572
if (level == 1) {
573
for (int e = 0; e < 512; e++) {
574
bool available = (table[e] & 1) != 0;
575
uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
576
if (!available)
577
continue;
578
ImGui::Text("Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
579
e, table[e], entry_virt_addr);
580
}
581
} else {
582
for (int e = 0; e < 512; e++) {
583
bool available = (table[e] & 1) != 0;
584
uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
585
if (available &&
586
ImGui::TreeNodeEx(&table[e],
587
available ? ImGuiTreeNodeFlags_Framed : 0,
588
"Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
589
e, table[e], entry_virt_addr)) {
590
display_pml4_level(mem, table[e] & ~0xffful, entry_virt_addr, level -1);
591
ImGui::TreePop();
592
}
593
}
594
}
595
}
596
597
static void
598
display_pml4_window(struct window *win)
599
{
600
struct pml4_window *window = (struct pml4_window *) win;
601
602
ImGui::Text("pml4: %" PRIx64, window->mem->pml4);
603
ImGui::BeginChild(ImGui::GetID("##block"));
604
display_pml4_level(window->mem, window->mem->pml4, 0, 4);
605
ImGui::EndChild();
606
}
607
608
static void
609
show_pml4_window(struct pml4_window *window, struct aub_mem *mem)
610
{
611
if (window->base.opened) {
612
window->base.opened = false;
613
return;
614
}
615
616
snprintf(window->base.name, sizeof(window->base.name),
617
"4-Level page tables##%p", window);
618
619
list_inithead(&window->base.parent_link);
620
window->base.position = ImVec2(-1, -1);
621
window->base.size = ImVec2(500, 600);
622
window->base.opened = true;
623
window->base.display = display_pml4_window;
624
window->base.destroy = destroy_window_noop;
625
626
window->mem = mem;
627
628
list_addtail(&window->base.link, &context.windows);
629
}
630
631
/* Batch decoding windows */
632
633
static void
634
display_decode_options(struct aub_viewer_decode_cfg *cfg)
635
{
636
char name[40];
637
snprintf(name, sizeof(name), "command filter##%p", &cfg->command_filter);
638
cfg->command_filter.Draw(name); ImGui::SameLine();
639
snprintf(name, sizeof(name), "field filter##%p", &cfg->field_filter);
640
cfg->field_filter.Draw(name); ImGui::SameLine();
641
if (ImGui::Button("Dwords")) cfg->show_dwords ^= 1;
642
}
643
644
static void
645
batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)
646
{
647
struct batch_window *window = (struct batch_window *) user_data;
648
struct shader_window *shader_window =
649
new_shader_window(&window->mem, address, shader_desc);
650
651
list_add(&shader_window->base.parent_link, &window->base.children_windows);
652
}
653
654
static void
655
batch_display_urb(void *user_data, const struct aub_decode_urb_stage_state *stages)
656
{
657
struct batch_window *window = (struct batch_window *) user_data;
658
struct urb_window *urb_window = new_urb_window(&window->decode_ctx, 0);
659
660
list_add(&urb_window->base.parent_link, &window->base.children_windows);
661
}
662
663
static void
664
batch_edit_address(void *user_data, uint64_t address, uint32_t len)
665
{
666
struct batch_window *window = (struct batch_window *) user_data;
667
struct edit_window *edit_window =
668
new_edit_window(&window->mem, address, len);
669
670
list_add(&edit_window->base.parent_link, &window->base.children_windows);
671
}
672
673
static struct intel_batch_decode_bo
674
batch_get_bo(void *user_data, bool ppgtt, uint64_t address)
675
{
676
struct batch_window *window = (struct batch_window *) user_data;
677
678
if (window->uses_ppgtt && ppgtt)
679
return aub_mem_get_ppgtt_bo(&window->mem, address);
680
else
681
return aub_mem_get_ggtt_bo(&window->mem, address);
682
}
683
684
static void
685
update_batch_window(struct batch_window *window, bool reset, int exec_idx)
686
{
687
if (reset)
688
aub_mem_fini(&window->mem);
689
aub_mem_init(&window->mem);
690
691
window->exec_idx = MAX2(MIN2(context.file->n_execs - 1, exec_idx), 0);
692
update_mem_for_exec(&window->mem, context.file, window->exec_idx);
693
}
694
695
static void
696
display_batch_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,
697
const void *data, uint32_t data_len)
698
{
699
struct batch_window *window = (struct batch_window *) user_data;
700
701
window->uses_ppgtt = false;
702
703
aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0, false);
704
}
705
706
static void
707
display_batch_execlist_write(void *user_data,
708
enum drm_i915_gem_engine_class engine,
709
uint64_t context_descriptor)
710
{
711
struct batch_window *window = (struct batch_window *) user_data;
712
713
const uint32_t pphwsp_size = 4096;
714
uint32_t pphwsp_addr = context_descriptor & 0xfffff000;
715
struct intel_batch_decode_bo pphwsp_bo =
716
aub_mem_get_ggtt_bo(&window->mem, pphwsp_addr);
717
uint32_t *context_img = (uint32_t *)((uint8_t *)pphwsp_bo.map +
718
(pphwsp_addr - pphwsp_bo.addr) +
719
pphwsp_size);
720
721
uint32_t ring_buffer_head = context_img[5];
722
uint32_t ring_buffer_tail = context_img[7];
723
uint32_t ring_buffer_start = context_img[9];
724
uint32_t ring_buffer_length = (context_img[11] & 0x1ff000) + 4096;
725
726
window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51];
727
728
struct intel_batch_decode_bo ring_bo =
729
aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start);
730
assert(ring_bo.size > 0);
731
void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head;
732
733
window->uses_ppgtt = true;
734
735
window->decode_ctx.engine = engine;
736
aub_viewer_render_batch(&window->decode_ctx, commands,
737
MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length),
738
ring_buffer_start + ring_buffer_head, true);
739
}
740
741
static void
742
display_batch_window(struct window *win)
743
{
744
struct batch_window *window = (struct batch_window *) win;
745
746
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
747
if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
748
display_decode_options(&window->decode_cfg);
749
ImGui::PopItemWidth();
750
751
if (ImGui::InputInt("Execbuf", &window->exec_idx))
752
update_batch_window(window, true, window->exec_idx);
753
754
if (window_has_ctrl_key('p'))
755
update_batch_window(window, true, window->exec_idx - 1);
756
if (window_has_ctrl_key('n'))
757
update_batch_window(window, true, window->exec_idx + 1);
758
759
ImGui::Text("execbuf %i", window->exec_idx);
760
if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window->pml4_window, &window->mem); }
761
762
ImGui::BeginChild(ImGui::GetID("##block"));
763
764
struct aub_read read = {};
765
read.user_data = window;
766
read.ring_write = display_batch_ring_write;
767
read.execlist_write = display_batch_execlist_write;
768
769
const uint8_t *iter = context.file->execs[window->exec_idx].start;
770
while (iter < context.file->execs[window->exec_idx].end) {
771
iter += aub_read_command(&read, iter,
772
context.file->execs[window->exec_idx].end - iter);
773
}
774
775
ImGui::EndChild();
776
}
777
778
static void
779
destroy_batch_window(struct window *win)
780
{
781
struct batch_window *window = (struct batch_window *) win;
782
783
aub_mem_fini(&window->mem);
784
785
/* This works because children windows are inserted at the back of the
786
* list, ensuring the deletion loop goes through the children after calling
787
* this function.
788
*/
789
list_for_each_entry(struct window, child_window,
790
&window->base.children_windows, parent_link)
791
child_window->opened = false;
792
window->pml4_window.base.opened = false;
793
794
free(window);
795
}
796
797
static void
798
new_batch_window(int exec_idx)
799
{
800
struct batch_window *window = xtzalloc(*window);
801
802
snprintf(window->base.name, sizeof(window->base.name),
803
"Batch view##%p", window);
804
805
list_inithead(&window->base.parent_link);
806
list_inithead(&window->base.children_windows);
807
window->base.position = ImVec2(-1, -1);
808
window->base.size = ImVec2(600, 700);
809
window->base.opened = true;
810
window->base.display = display_batch_window;
811
window->base.destroy = destroy_batch_window;
812
813
window->collapsed = true;
814
window->decode_cfg = aub_viewer_decode_cfg();
815
816
aub_viewer_decode_ctx_init(&window->decode_ctx,
817
&context.cfg,
818
&window->decode_cfg,
819
&context.file->devinfo,
820
context.file->spec,
821
batch_get_bo,
822
NULL,
823
window);
824
window->decode_ctx.display_shader = batch_display_shader;
825
window->decode_ctx.display_urb = batch_display_urb;
826
window->decode_ctx.edit_address = batch_edit_address;
827
828
update_batch_window(window, false, exec_idx);
829
830
list_addtail(&window->base.link, &context.windows);
831
}
832
833
/**/
834
835
static void
836
display_registers_window(struct window *win)
837
{
838
static struct ImGuiTextFilter filter;
839
if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
840
filter.Draw();
841
842
ImGui::BeginChild(ImGui::GetID("##block"));
843
hash_table_foreach(context.file->spec->registers_by_name, entry) {
844
struct intel_group *reg = (struct intel_group *) entry->data;
845
if (filter.PassFilter(reg->name) &&
846
ImGui::CollapsingHeader(reg->name)) {
847
const struct intel_field *field = reg->fields;
848
while (field) {
849
ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
850
field = field->next;
851
}
852
}
853
}
854
ImGui::EndChild();
855
}
856
857
static void
858
show_register_window(void)
859
{
860
struct window *window = &context.registers_window;
861
862
if (window->opened) {
863
window->opened = false;
864
return;
865
}
866
867
snprintf(window->name, sizeof(window->name), "Registers");
868
869
list_inithead(&window->parent_link);
870
window->position = ImVec2(-1, -1);
871
window->size = ImVec2(200, 400);
872
window->opened = true;
873
window->display = display_registers_window;
874
window->destroy = destroy_window_noop;
875
876
list_addtail(&window->link, &context.windows);
877
}
878
879
static void
880
display_commands_window(struct window *win)
881
{
882
static struct ImGuiTextFilter cmd_filter;
883
if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
884
cmd_filter.Draw("name filter");
885
static struct ImGuiTextFilter field_filter;
886
field_filter.Draw("field filter");
887
888
static char opcode_str[9] = { 0, };
889
ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str),
890
ImGuiInputTextFlags_CharsHexadecimal);
891
size_t opcode_len = strlen(opcode_str);
892
uint64_t opcode = strtol(opcode_str, NULL, 16);
893
894
static bool show_dwords = true;
895
if (ImGui::Button("Dwords")) show_dwords ^= 1;
896
897
ImGui::BeginChild(ImGui::GetID("##block"));
898
hash_table_foreach(context.file->spec->commands, entry) {
899
struct intel_group *cmd = (struct intel_group *) entry->data;
900
if ((cmd_filter.PassFilter(cmd->name) &&
901
(opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&
902
ImGui::CollapsingHeader(cmd->name)) {
903
const struct intel_field *field = cmd->fields;
904
int32_t last_dword = -1;
905
while (field) {
906
if (show_dwords && field->start / 32 != last_dword) {
907
for (last_dword = MAX2(0, last_dword + 1);
908
last_dword < field->start / 32; last_dword++) {
909
ImGui::TextColored(context.cfg.dwords_color,
910
"Dword %d", last_dword);
911
}
912
ImGui::TextColored(context.cfg.dwords_color, "Dword %d", last_dword);
913
}
914
if (field_filter.PassFilter(field->name))
915
ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
916
field = field->next;
917
}
918
}
919
}
920
hash_table_foreach(context.file->spec->structs, entry) {
921
struct intel_group *cmd = (struct intel_group *) entry->data;
922
if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
923
ImGui::CollapsingHeader(cmd->name)) {
924
const struct intel_field *field = cmd->fields;
925
int32_t last_dword = -1;
926
while (field) {
927
if (show_dwords && field->start / 32 != last_dword) {
928
last_dword = field->start / 32;
929
ImGui::TextColored(context.cfg.dwords_color,
930
"Dword %d", last_dword);
931
}
932
if (field_filter.PassFilter(field->name))
933
ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
934
field = field->next;
935
}
936
}
937
}
938
ImGui::EndChild();
939
}
940
941
static void
942
show_commands_window(void)
943
{
944
struct window *window = &context.commands_window;
945
946
if (window->opened) {
947
window->opened = false;
948
return;
949
}
950
951
snprintf(window->name, sizeof(window->name), "Commands & structs");
952
953
list_inithead(&window->parent_link);
954
window->position = ImVec2(-1, -1);
955
window->size = ImVec2(300, 400);
956
window->opened = true;
957
window->display = display_commands_window;
958
window->destroy = destroy_window_noop;
959
960
list_addtail(&window->link, &context.windows);
961
}
962
963
/* Main window */
964
965
static const char *
966
human_size(size_t size)
967
{
968
unsigned divisions = 0;
969
double v = size;
970
double divider = 1024;
971
while (v >= divider) {
972
v /= divider;
973
divisions++;
974
}
975
976
static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
977
static char result[20];
978
snprintf(result, sizeof(result), "%.2f %s",
979
v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);
980
return result;
981
}
982
983
static void
984
display_aubfile_window(struct window *win)
985
{
986
ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |
987
ImGuiColorEditFlags_NoLabel |
988
ImGuiColorEditFlags_NoInputs);
989
struct aub_viewer_cfg *cfg = &context.cfg;
990
991
ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine();
992
ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine();
993
ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine();
994
ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine();
995
ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine();
996
ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();
997
998
if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
999
if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
1000
if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
1001
1002
if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
1003
1004
ImGui::Text("File name: %s", context.input_file);
1005
ImGui::Text("File size: %s", human_size(context.file->end - context.file->map));
1006
ImGui::Text("Execbufs %u", context.file->n_execs);
1007
ImGui::Text("PCI ID: 0x%x", context.file->pci_id);
1008
ImGui::Text("Application name: %s", context.file->app_name);
1009
ImGui::Text("%s", intel_get_device_name(context.file->pci_id));
1010
1011
ImGui::SetNextWindowContentWidth(500);
1012
if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
1013
ImGui::Text("Some global keybindings:");
1014
ImGui::Separator();
1015
1016
static const char *texts[] = {
1017
"Ctrl-h", "show this screen",
1018
"Ctrl-c", "show commands list",
1019
"Ctrl-r", "show registers list",
1020
"Ctrl-b", "new batch window",
1021
"Ctrl-p/n", "switch to previous/next batch buffer",
1022
"Ctrl-Tab", "switch focus between window",
1023
"Ctrl-left/right", "align window to the side of the screen",
1024
};
1025
float align = 0.0f;
1026
for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)
1027
align = MAX2(align, ImGui::CalcTextSize(texts[i]).x);
1028
align += ImGui::GetStyle().WindowPadding.x + 10;
1029
1030
for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {
1031
ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);
1032
}
1033
1034
if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
1035
ImGui::CloseCurrentPopup();
1036
ImGui::EndPopup();
1037
}
1038
}
1039
1040
static void
1041
show_aubfile_window(void)
1042
{
1043
struct window *window = &context.file_window;
1044
1045
if (window->opened)
1046
return;
1047
1048
snprintf(window->name, sizeof(window->name),
1049
"Aubinator Viewer: Intel AUB file decoder/editor");
1050
1051
list_inithead(&window->parent_link);
1052
window->size = ImVec2(-1, 250);
1053
window->position = ImVec2(0, 0);
1054
window->opened = true;
1055
window->display = display_aubfile_window;
1056
window->destroy = NULL;
1057
1058
list_addtail(&window->link, &context.windows);
1059
}
1060
1061
/* Main redrawing */
1062
1063
static void
1064
display_windows(void)
1065
{
1066
/* Start by disposing closed windows, we don't want to destroy windows that
1067
* have already been scheduled to be painted. So destroy always happens on
1068
* the next draw cycle, prior to any drawing.
1069
*/
1070
list_for_each_entry_safe(struct window, window, &context.windows, link) {
1071
if (window->opened)
1072
continue;
1073
1074
/* Can't close this one. */
1075
if (window == &context.file_window) {
1076
window->opened = true;
1077
continue;
1078
}
1079
1080
list_del(&window->link);
1081
list_del(&window->parent_link);
1082
if (window->destroy)
1083
window->destroy(window);
1084
}
1085
1086
list_for_each_entry_safe(struct window, window, &context.windows, link) {
1087
ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver);
1088
ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver);
1089
if (ImGui::Begin(window->name, &window->opened)) {
1090
window->display(window);
1091
window->position = ImGui::GetWindowPos();
1092
window->size = ImGui::GetWindowSize();
1093
}
1094
if (window_has_ctrl_key('w'))
1095
window->opened = false;
1096
ImGui::End();
1097
}
1098
}
1099
1100
static void
1101
repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context)
1102
{
1103
ImGui_ImplOpenGL3_NewFrame();
1104
ImGui_ImplGtk3_NewFrame();
1105
ImGui::NewFrame();
1106
1107
display_windows();
1108
1109
ImGui::EndFrame();
1110
ImGui::Render();
1111
1112
glClearColor(context.cfg.clear_color.Value.x,
1113
context.cfg.clear_color.Value.y,
1114
context.cfg.clear_color.Value.z, 1.0);
1115
glClear(GL_COLOR_BUFFER_BIT);
1116
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1117
}
1118
1119
static void
1120
realize_area(GtkGLArea *area)
1121
{
1122
ImGui::CreateContext();
1123
ImGui_ImplGtk3_Init(GTK_WIDGET(area), true);
1124
ImGui_ImplOpenGL3_Init("#version 130");
1125
1126
list_inithead(&context.windows);
1127
1128
ImGui::StyleColorsDark();
1129
context.cfg = aub_viewer_cfg();
1130
1131
ImGuiIO& io = ImGui::GetIO();
1132
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
1133
}
1134
1135
static void
1136
unrealize_area(GtkGLArea *area)
1137
{
1138
gtk_gl_area_make_current(area);
1139
1140
ImGui_ImplOpenGL3_Shutdown();
1141
ImGui_ImplGtk3_Shutdown();
1142
ImGui::DestroyContext();
1143
}
1144
1145
static void
1146
size_allocate_area(GtkGLArea *area,
1147
GdkRectangle *allocation,
1148
gpointer user_data)
1149
{
1150
if (!gtk_widget_get_realized(GTK_WIDGET(area)))
1151
return;
1152
1153
/* We want to catch only initial size allocate. */
1154
g_signal_handlers_disconnect_by_func(area,
1155
(gpointer) size_allocate_area,
1156
user_data);
1157
show_aubfile_window();
1158
}
1159
1160
static void
1161
print_help(const char *progname, FILE *file)
1162
{
1163
fprintf(file,
1164
"Usage: %s [OPTION]... FILE\n"
1165
"Decode aub file contents from FILE.\n\n"
1166
" --help display this help and exit\n"
1167
" -x, --xml=DIR load hardware xml description from directory DIR\n",
1168
progname);
1169
}
1170
1171
int main(int argc, char *argv[])
1172
{
1173
int c, i;
1174
bool help = false;
1175
const struct option aubinator_opts[] = {
1176
{ "help", no_argument, (int *) &help, true },
1177
{ "xml", required_argument, NULL, 'x' },
1178
{ NULL, 0, NULL, 0 }
1179
};
1180
1181
memset(&context, 0, sizeof(context));
1182
1183
i = 0;
1184
while ((c = getopt_long(argc, argv, "x:s:", aubinator_opts, &i)) != -1) {
1185
switch (c) {
1186
case 'x':
1187
context.xml_path = strdup(optarg);
1188
break;
1189
default:
1190
break;
1191
}
1192
}
1193
1194
if (optind < argc)
1195
context.input_file = argv[optind];
1196
1197
if (help || !context.input_file) {
1198
print_help(argv[0], stderr);
1199
exit(0);
1200
}
1201
1202
context.file = aub_file_open(context.input_file);
1203
1204
gtk_init(NULL, NULL);
1205
1206
context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1207
gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Aubinator Viewer");
1208
g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
1209
gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720);
1210
1211
GtkWidget* gl_area = gtk_gl_area_new();
1212
g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL);
1213
g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL);
1214
g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL);
1215
g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);
1216
gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
1217
1218
gtk_widget_show_all(context.gtk_window);
1219
1220
gtk_main();
1221
1222
free(context.xml_path);
1223
1224
return EXIT_SUCCESS;
1225
}
1226
1227