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