intel/perf: export performance counters sorted by [group|set] and name
[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_from_pci_id(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/imgui.h"
225 #include "imgui/imgui_memory_editor.h"
226 #include "imgui_impl_gtk3.h"
227 #include "imgui_impl_opengl3.h"
228
229 #include "aubinator_viewer.h"
230 #include "aubinator_viewer_urb.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 thread_local ImGuiContext* __MesaImGui;
327
328 static int
329 map_key(int k)
330 {
331 return ImGuiKey_COUNT + k;
332 }
333
334 static bool
335 has_ctrl_key(int key)
336 {
337 return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));
338 }
339
340 static bool
341 window_has_ctrl_key(int key)
342 {
343 return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
344 }
345
346 static void
347 destroy_window_noop(struct window *win)
348 {
349 }
350
351 /* Shader windows */
352
353 static void
354 display_shader_window(struct window *win)
355 {
356 struct shader_window *window = (struct shader_window *) win;
357
358 if (window->shader) {
359 ImGui::InputTextMultiline("Assembly",
360 window->shader, window->shader_size,
361 ImGui::GetContentRegionAvail(),
362 ImGuiInputTextFlags_ReadOnly);
363 } else {
364 ImGui::Text("Shader not available");
365 }
366 }
367
368 static void
369 destroy_shader_window(struct window *win)
370 {
371 struct shader_window *window = (struct shader_window *) win;
372
373 free(window->shader);
374 free(window);
375 }
376
377 static struct shader_window *
378 new_shader_window(struct aub_mem *mem, uint64_t address, const char *desc)
379 {
380 struct shader_window *window = xtzalloc(*window);
381
382 snprintf(window->base.name, sizeof(window->base.name),
383 "%s (0x%" PRIx64 ")##%p", desc, address, window);
384
385 list_inithead(&window->base.parent_link);
386 window->base.position = ImVec2(-1, -1);
387 window->base.size = ImVec2(700, 300);
388 window->base.opened = true;
389 window->base.display = display_shader_window;
390 window->base.destroy = destroy_shader_window;
391
392 struct gen_batch_decode_bo shader_bo =
393 aub_mem_get_ppgtt_bo(mem, address);
394 if (shader_bo.map) {
395 FILE *f = open_memstream(&window->shader, &window->shader_size);
396 if (f) {
397 gen_disasm_disassemble(context.file->disasm,
398 (const uint8_t *) shader_bo.map +
399 (address - shader_bo.addr), 0, f);
400 fclose(f);
401 }
402 }
403
404 list_addtail(&window->base.link, &context.windows);
405
406 return window;
407 }
408
409 /* URB windows */
410
411 static void
412 display_urb_window(struct window *win)
413 {
414 struct urb_window *window = (struct urb_window *) win;
415 static const char *stages[] = {
416 [AUB_DECODE_STAGE_VS] = "VS",
417 [AUB_DECODE_STAGE_HS] = "HS",
418 [AUB_DECODE_STAGE_DS] = "DS",
419 [AUB_DECODE_STAGE_GS] = "GS",
420 [AUB_DECODE_STAGE_PS] = "PS",
421 [AUB_DECODE_STAGE_CS] = "CS",
422 };
423
424 ImGui::Text("URB allocation:");
425 window->urb_view.DrawAllocation("##urb",
426 ARRAY_SIZE(window->urb_stages),
427 window->end_urb_offset,
428 stages,
429 &window->urb_stages[0]);
430 }
431
432 static void
433 destroy_urb_window(struct window *win)
434 {
435 struct urb_window *window = (struct urb_window *) win;
436
437 free(window);
438 }
439
440 static struct urb_window *
441 new_urb_window(struct aub_viewer_decode_ctx *decode_ctx, uint64_t address)
442 {
443 struct urb_window *window = xtzalloc(*window);
444
445 snprintf(window->base.name, sizeof(window->base.name),
446 "URB view (0x%" PRIx64 ")##%p", address, window);
447
448 list_inithead(&window->base.parent_link);
449 window->base.position = ImVec2(-1, -1);
450 window->base.size = ImVec2(700, 300);
451 window->base.opened = true;
452 window->base.display = display_urb_window;
453 window->base.destroy = destroy_urb_window;
454
455 window->end_urb_offset = decode_ctx->end_urb_offset;
456 memcpy(window->urb_stages, decode_ctx->urb_stages, sizeof(window->urb_stages));
457 window->urb_view = AubinatorViewerUrb();
458
459 list_addtail(&window->base.link, &context.windows);
460
461 return window;
462 }
463
464 /* Memory editor windows */
465
466 static uint8_t
467 read_edit_window(const uint8_t *data, size_t off)
468 {
469 struct edit_window *window = (struct edit_window *) data;
470
471 return *((const uint8_t *) window->gtt_bo.map + window->gtt_offset + off);
472 }
473
474 static void
475 write_edit_window(uint8_t *data, size_t off, uint8_t d)
476 {
477 struct edit_window *window = (struct edit_window *) data;
478 uint8_t *gtt = (uint8_t *) window->gtt_bo.map + window->gtt_offset + off;
479 uint8_t *aub = (uint8_t *) window->aub_bo.map + window->aub_offset + off;
480
481 *gtt = *aub = d;
482 }
483
484 static void
485 display_edit_window(struct window *win)
486 {
487 struct edit_window *window = (struct edit_window *) win;
488
489 if (window->aub_bo.map && window->gtt_bo.map) {
490 ImGui::BeginChild(ImGui::GetID("##block"));
491 window->editor.DrawContents((uint8_t *) window,
492 MIN3(window->len,
493 window->gtt_bo.size - window->gtt_offset,
494 window->aub_bo.size - window->aub_offset),
495 window->address);
496 ImGui::EndChild();
497 } else {
498 ImGui::Text("Memory view at 0x%" PRIx64 " not available", window->address);
499 }
500 }
501
502 static void
503 destroy_edit_window(struct window *win)
504 {
505 struct edit_window *window = (struct edit_window *) win;
506
507 if (window->aub_bo.map)
508 mprotect((void *) window->aub_bo.map, 4096, PROT_READ);
509 free(window);
510 }
511
512 static struct edit_window *
513 new_edit_window(struct aub_mem *mem, uint64_t address, uint32_t len)
514 {
515 struct edit_window *window = xtzalloc(*window);
516
517 snprintf(window->base.name, sizeof(window->base.name),
518 "Editing aub at 0x%" PRIx64 "##%p", address, window);
519
520 list_inithead(&window->base.parent_link);
521 window->base.position = ImVec2(-1, -1);
522 window->base.size = ImVec2(500, 600);
523 window->base.opened = true;
524 window->base.display = display_edit_window;
525 window->base.destroy = destroy_edit_window;
526
527 window->mem = mem;
528 window->address = address;
529 window->aub_bo = aub_mem_get_ppgtt_addr_aub_data(mem, address);
530 window->gtt_bo = aub_mem_get_ppgtt_addr_data(mem, address);
531 window->len = len;
532 window->editor = MemoryEditor();
533 window->editor.OptShowDataPreview = true;
534 window->editor.OptShowAscii = false;
535 window->editor.ReadFn = read_edit_window;
536 window->editor.WriteFn = write_edit_window;
537
538 if (window->aub_bo.map) {
539 uint64_t unaligned_map = (uint64_t) window->aub_bo.map;
540 window->aub_bo.map = (const void *)(unaligned_map & ~0xffful);
541 window->aub_offset = unaligned_map - (uint64_t) window->aub_bo.map;
542
543 if (mprotect((void *) window->aub_bo.map, window->aub_bo.size, PROT_READ | PROT_WRITE) != 0) {
544 window->aub_bo.map = NULL;
545 }
546 }
547
548 window->gtt_offset = address - window->gtt_bo.addr;
549
550 list_addtail(&window->base.link, &context.windows);
551
552 return window;
553 }
554
555 /* 4 level page table walk windows */
556
557 static void
558 display_pml4_level(struct aub_mem *mem, uint64_t table_addr, uint64_t table_virt_addr, int level)
559 {
560 if (level == 0)
561 return;
562
563 struct gen_batch_decode_bo table_bo =
564 aub_mem_get_phys_addr_data(mem, table_addr);
565 const uint64_t *table = (const uint64_t *) ((const uint8_t *) table_bo.map +
566 table_addr - table_bo.addr);
567 if (!table) {
568 ImGui::TextColored(context.cfg.missing_color, "Page not available");
569 return;
570 }
571
572 uint64_t addr_increment = 1ULL << (12 + 9 * (level - 1));
573
574 if (level == 1) {
575 for (int e = 0; e < 512; e++) {
576 bool available = (table[e] & 1) != 0;
577 uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
578 if (!available)
579 continue;
580 ImGui::Text("Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
581 e, table[e], entry_virt_addr);
582 }
583 } else {
584 for (int e = 0; e < 512; e++) {
585 bool available = (table[e] & 1) != 0;
586 uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
587 if (available &&
588 ImGui::TreeNodeEx(&table[e],
589 available ? ImGuiTreeNodeFlags_Framed : 0,
590 "Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
591 e, table[e], entry_virt_addr)) {
592 display_pml4_level(mem, table[e] & ~0xffful, entry_virt_addr, level -1);
593 ImGui::TreePop();
594 }
595 }
596 }
597 }
598
599 static void
600 display_pml4_window(struct window *win)
601 {
602 struct pml4_window *window = (struct pml4_window *) win;
603
604 ImGui::Text("pml4: %" PRIx64, window->mem->pml4);
605 ImGui::BeginChild(ImGui::GetID("##block"));
606 display_pml4_level(window->mem, window->mem->pml4, 0, 4);
607 ImGui::EndChild();
608 }
609
610 static void
611 show_pml4_window(struct pml4_window *window, struct aub_mem *mem)
612 {
613 if (window->base.opened) {
614 window->base.opened = false;
615 return;
616 }
617
618 snprintf(window->base.name, sizeof(window->base.name),
619 "4-Level page tables##%p", window);
620
621 list_inithead(&window->base.parent_link);
622 window->base.position = ImVec2(-1, -1);
623 window->base.size = ImVec2(500, 600);
624 window->base.opened = true;
625 window->base.display = display_pml4_window;
626 window->base.destroy = destroy_window_noop;
627
628 window->mem = mem;
629
630 list_addtail(&window->base.link, &context.windows);
631 }
632
633 /* Batch decoding windows */
634
635 static void
636 display_decode_options(struct aub_viewer_decode_cfg *cfg)
637 {
638 char name[40];
639 snprintf(name, sizeof(name), "command filter##%p", &cfg->command_filter);
640 cfg->command_filter.Draw(name); ImGui::SameLine();
641 snprintf(name, sizeof(name), "field filter##%p", &cfg->field_filter);
642 cfg->field_filter.Draw(name); ImGui::SameLine();
643 if (ImGui::Button("Dwords")) cfg->show_dwords ^= 1;
644 }
645
646 static void
647 batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)
648 {
649 struct batch_window *window = (struct batch_window *) user_data;
650 struct shader_window *shader_window =
651 new_shader_window(&window->mem, address, shader_desc);
652
653 list_add(&shader_window->base.parent_link, &window->base.children_windows);
654 }
655
656 static void
657 batch_display_urb(void *user_data, const struct aub_decode_urb_stage_state *stages)
658 {
659 struct batch_window *window = (struct batch_window *) user_data;
660 struct urb_window *urb_window = new_urb_window(&window->decode_ctx, 0);
661
662 list_add(&urb_window->base.parent_link, &window->base.children_windows);
663 }
664
665 static void
666 batch_edit_address(void *user_data, uint64_t address, uint32_t len)
667 {
668 struct batch_window *window = (struct batch_window *) user_data;
669 struct edit_window *edit_window =
670 new_edit_window(&window->mem, address, len);
671
672 list_add(&edit_window->base.parent_link, &window->base.children_windows);
673 }
674
675 static struct gen_batch_decode_bo
676 batch_get_bo(void *user_data, bool ppgtt, uint64_t address)
677 {
678 struct batch_window *window = (struct batch_window *) user_data;
679
680 if (window->uses_ppgtt && ppgtt)
681 return aub_mem_get_ppgtt_bo(&window->mem, address);
682 else
683 return aub_mem_get_ggtt_bo(&window->mem, address);
684 }
685
686 static void
687 update_batch_window(struct batch_window *window, bool reset, int exec_idx)
688 {
689 if (reset)
690 aub_mem_fini(&window->mem);
691 aub_mem_init(&window->mem);
692
693 window->exec_idx = MAX2(MIN2(context.file->n_execs - 1, exec_idx), 0);
694 update_mem_for_exec(&window->mem, context.file, window->exec_idx);
695 }
696
697 static void
698 display_batch_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,
699 const void *data, uint32_t data_len)
700 {
701 struct batch_window *window = (struct batch_window *) user_data;
702
703 window->uses_ppgtt = false;
704
705 aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0, false);
706 }
707
708 static void
709 display_batch_execlist_write(void *user_data,
710 enum drm_i915_gem_engine_class engine,
711 uint64_t context_descriptor)
712 {
713 struct batch_window *window = (struct batch_window *) user_data;
714
715 const uint32_t pphwsp_size = 4096;
716 uint32_t pphwsp_addr = context_descriptor & 0xfffff000;
717 struct gen_batch_decode_bo pphwsp_bo =
718 aub_mem_get_ggtt_bo(&window->mem, pphwsp_addr);
719 uint32_t *context_img = (uint32_t *)((uint8_t *)pphwsp_bo.map +
720 (pphwsp_addr - pphwsp_bo.addr) +
721 pphwsp_size);
722
723 uint32_t ring_buffer_head = context_img[5];
724 uint32_t ring_buffer_tail = context_img[7];
725 uint32_t ring_buffer_start = context_img[9];
726 uint32_t ring_buffer_length = (context_img[11] & 0x1ff000) + 4096;
727
728 window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51];
729
730 struct gen_batch_decode_bo ring_bo =
731 aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start);
732 assert(ring_bo.size > 0);
733 void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head;
734
735 window->uses_ppgtt = true;
736
737 window->decode_ctx.engine = engine;
738 aub_viewer_render_batch(&window->decode_ctx, commands,
739 MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length),
740 ring_buffer_start + ring_buffer_head, true);
741 }
742
743 static void
744 display_batch_window(struct window *win)
745 {
746 struct batch_window *window = (struct batch_window *) win;
747
748 ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
749 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
750 display_decode_options(&window->decode_cfg);
751 ImGui::PopItemWidth();
752
753 if (ImGui::InputInt("Execbuf", &window->exec_idx))
754 update_batch_window(window, true, window->exec_idx);
755
756 if (window_has_ctrl_key('p'))
757 update_batch_window(window, true, window->exec_idx - 1);
758 if (window_has_ctrl_key('n'))
759 update_batch_window(window, true, window->exec_idx + 1);
760
761 ImGui::Text("execbuf %i", window->exec_idx);
762 if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window->pml4_window, &window->mem); }
763
764 ImGui::BeginChild(ImGui::GetID("##block"));
765
766 struct aub_read read = {};
767 read.user_data = window;
768 read.ring_write = display_batch_ring_write;
769 read.execlist_write = display_batch_execlist_write;
770
771 const uint8_t *iter = context.file->execs[window->exec_idx].start;
772 while (iter < context.file->execs[window->exec_idx].end) {
773 iter += aub_read_command(&read, iter,
774 context.file->execs[window->exec_idx].end - iter);
775 }
776
777 ImGui::EndChild();
778 }
779
780 static void
781 destroy_batch_window(struct window *win)
782 {
783 struct batch_window *window = (struct batch_window *) win;
784
785 aub_mem_fini(&window->mem);
786
787 /* This works because children windows are inserted at the back of the
788 * list, ensuring the deletion loop goes through the children after calling
789 * this function.
790 */
791 list_for_each_entry(struct window, child_window,
792 &window->base.children_windows, parent_link)
793 child_window->opened = false;
794 window->pml4_window.base.opened = false;
795
796 free(window);
797 }
798
799 static void
800 new_batch_window(int exec_idx)
801 {
802 struct batch_window *window = xtzalloc(*window);
803
804 snprintf(window->base.name, sizeof(window->base.name),
805 "Batch view##%p", window);
806
807 list_inithead(&window->base.parent_link);
808 list_inithead(&window->base.children_windows);
809 window->base.position = ImVec2(-1, -1);
810 window->base.size = ImVec2(600, 700);
811 window->base.opened = true;
812 window->base.display = display_batch_window;
813 window->base.destroy = destroy_batch_window;
814
815 window->collapsed = true;
816 window->decode_cfg = aub_viewer_decode_cfg();
817
818 aub_viewer_decode_ctx_init(&window->decode_ctx,
819 &context.cfg,
820 &window->decode_cfg,
821 context.file->spec,
822 context.file->disasm,
823 batch_get_bo,
824 NULL,
825 window);
826 window->decode_ctx.display_shader = batch_display_shader;
827 window->decode_ctx.display_urb = batch_display_urb;
828 window->decode_ctx.edit_address = batch_edit_address;
829
830 update_batch_window(window, false, exec_idx);
831
832 list_addtail(&window->base.link, &context.windows);
833 }
834
835 /**/
836
837 static void
838 display_registers_window(struct window *win)
839 {
840 static struct ImGuiTextFilter filter;
841 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
842 filter.Draw();
843
844 ImGui::BeginChild(ImGui::GetID("##block"));
845 hash_table_foreach(context.file->spec->registers_by_name, entry) {
846 struct gen_group *reg = (struct gen_group *) entry->data;
847 if (filter.PassFilter(reg->name) &&
848 ImGui::CollapsingHeader(reg->name)) {
849 const struct gen_field *field = reg->fields;
850 while (field) {
851 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
852 field = field->next;
853 }
854 }
855 }
856 ImGui::EndChild();
857 }
858
859 static void
860 show_register_window(void)
861 {
862 struct window *window = &context.registers_window;
863
864 if (window->opened) {
865 window->opened = false;
866 return;
867 }
868
869 snprintf(window->name, sizeof(window->name), "Registers");
870
871 list_inithead(&window->parent_link);
872 window->position = ImVec2(-1, -1);
873 window->size = ImVec2(200, 400);
874 window->opened = true;
875 window->display = display_registers_window;
876 window->destroy = destroy_window_noop;
877
878 list_addtail(&window->link, &context.windows);
879 }
880
881 static void
882 display_commands_window(struct window *win)
883 {
884 static struct ImGuiTextFilter cmd_filter;
885 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
886 cmd_filter.Draw("name filter");
887 static struct ImGuiTextFilter field_filter;
888 field_filter.Draw("field filter");
889
890 static char opcode_str[9] = { 0, };
891 ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str),
892 ImGuiInputTextFlags_CharsHexadecimal);
893 size_t opcode_len = strlen(opcode_str);
894 uint64_t opcode = strtol(opcode_str, NULL, 16);
895
896 static bool show_dwords = true;
897 if (ImGui::Button("Dwords")) show_dwords ^= 1;
898
899 ImGui::BeginChild(ImGui::GetID("##block"));
900 hash_table_foreach(context.file->spec->commands, entry) {
901 struct gen_group *cmd = (struct gen_group *) entry->data;
902 if ((cmd_filter.PassFilter(cmd->name) &&
903 (opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&
904 ImGui::CollapsingHeader(cmd->name)) {
905 const struct gen_field *field = cmd->fields;
906 int32_t last_dword = -1;
907 while (field) {
908 if (show_dwords && field->start / 32 != last_dword) {
909 for (last_dword = MAX2(0, last_dword + 1);
910 last_dword < field->start / 32; last_dword++) {
911 ImGui::TextColored(context.cfg.dwords_color,
912 "Dword %d", last_dword);
913 }
914 ImGui::TextColored(context.cfg.dwords_color, "Dword %d", last_dword);
915 }
916 if (field_filter.PassFilter(field->name))
917 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
918 field = field->next;
919 }
920 }
921 }
922 hash_table_foreach(context.file->spec->structs, entry) {
923 struct gen_group *cmd = (struct gen_group *) entry->data;
924 if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
925 ImGui::CollapsingHeader(cmd->name)) {
926 const struct gen_field *field = cmd->fields;
927 int32_t last_dword = -1;
928 while (field) {
929 if (show_dwords && field->start / 32 != last_dword) {
930 last_dword = field->start / 32;
931 ImGui::TextColored(context.cfg.dwords_color,
932 "Dword %d", last_dword);
933 }
934 if (field_filter.PassFilter(field->name))
935 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
936 field = field->next;
937 }
938 }
939 }
940 ImGui::EndChild();
941 }
942
943 static void
944 show_commands_window(void)
945 {
946 struct window *window = &context.commands_window;
947
948 if (window->opened) {
949 window->opened = false;
950 return;
951 }
952
953 snprintf(window->name, sizeof(window->name), "Commands & structs");
954
955 list_inithead(&window->parent_link);
956 window->position = ImVec2(-1, -1);
957 window->size = ImVec2(300, 400);
958 window->opened = true;
959 window->display = display_commands_window;
960 window->destroy = destroy_window_noop;
961
962 list_addtail(&window->link, &context.windows);
963 }
964
965 /* Main window */
966
967 static const char *
968 human_size(size_t size)
969 {
970 unsigned divisions = 0;
971 double v = size;
972 double divider = 1024;
973 while (v >= divider) {
974 v /= divider;
975 divisions++;
976 }
977
978 static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
979 static char result[20];
980 snprintf(result, sizeof(result), "%.2f %s",
981 v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);
982 return result;
983 }
984
985 static void
986 display_aubfile_window(struct window *win)
987 {
988 ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |
989 ImGuiColorEditFlags_NoLabel |
990 ImGuiColorEditFlags_NoInputs);
991 struct aub_viewer_cfg *cfg = &context.cfg;
992
993 ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine();
994 ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine();
995 ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine();
996 ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine();
997 ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine();
998 ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();
999
1000 if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
1001 if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
1002 if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
1003
1004 if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
1005
1006 ImGui::Text("File name: %s", context.input_file);
1007 ImGui::Text("File size: %s", human_size(context.file->end - context.file->map));
1008 ImGui::Text("Execbufs %u", context.file->n_execs);
1009 ImGui::Text("PCI ID: 0x%x", context.file->pci_id);
1010 ImGui::Text("Application name: %s", context.file->app_name);
1011 ImGui::Text("%s", gen_get_device_name(context.file->pci_id));
1012
1013 ImGui::SetNextWindowContentWidth(500);
1014 if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
1015 ImGui::Text("Some global keybindings:");
1016 ImGui::Separator();
1017
1018 static const char *texts[] = {
1019 "Ctrl-h", "show this screen",
1020 "Ctrl-c", "show commands list",
1021 "Ctrl-r", "show registers list",
1022 "Ctrl-b", "new batch window",
1023 "Ctrl-p/n", "switch to previous/next batch buffer",
1024 "Ctrl-Tab", "switch focus between window",
1025 "Ctrl-left/right", "align window to the side of the screen",
1026 };
1027 float align = 0.0f;
1028 for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)
1029 align = MAX2(align, ImGui::CalcTextSize(texts[i]).x);
1030 align += ImGui::GetStyle().WindowPadding.x + 10;
1031
1032 for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {
1033 ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);
1034 }
1035
1036 if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
1037 ImGui::CloseCurrentPopup();
1038 ImGui::EndPopup();
1039 }
1040 }
1041
1042 static void
1043 show_aubfile_window(void)
1044 {
1045 struct window *window = &context.file_window;
1046
1047 if (window->opened)
1048 return;
1049
1050 snprintf(window->name, sizeof(window->name),
1051 "Aubinator Viewer: Intel AUB file decoder/editor");
1052
1053 list_inithead(&window->parent_link);
1054 window->size = ImVec2(-1, 250);
1055 window->position = ImVec2(0, 0);
1056 window->opened = true;
1057 window->display = display_aubfile_window;
1058 window->destroy = NULL;
1059
1060 list_addtail(&window->link, &context.windows);
1061 }
1062
1063 /* Main redrawing */
1064
1065 static void
1066 display_windows(void)
1067 {
1068 /* Start by disposing closed windows, we don't want to destroy windows that
1069 * have already been scheduled to be painted. So destroy always happens on
1070 * the next draw cycle, prior to any drawing.
1071 */
1072 list_for_each_entry_safe(struct window, window, &context.windows, link) {
1073 if (window->opened)
1074 continue;
1075
1076 /* Can't close this one. */
1077 if (window == &context.file_window) {
1078 window->opened = true;
1079 continue;
1080 }
1081
1082 list_del(&window->link);
1083 list_del(&window->parent_link);
1084 if (window->destroy)
1085 window->destroy(window);
1086 }
1087
1088 list_for_each_entry_safe(struct window, window, &context.windows, link) {
1089 ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver);
1090 ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver);
1091 if (ImGui::Begin(window->name, &window->opened)) {
1092 window->display(window);
1093 window->position = ImGui::GetWindowPos();
1094 window->size = ImGui::GetWindowSize();
1095 }
1096 if (window_has_ctrl_key('w'))
1097 window->opened = false;
1098 ImGui::End();
1099 }
1100 }
1101
1102 static void
1103 repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context)
1104 {
1105 ImGui_ImplOpenGL3_NewFrame();
1106 ImGui_ImplGtk3_NewFrame();
1107 ImGui::NewFrame();
1108
1109 display_windows();
1110
1111 ImGui::EndFrame();
1112 ImGui::Render();
1113
1114 glClearColor(context.cfg.clear_color.Value.x,
1115 context.cfg.clear_color.Value.y,
1116 context.cfg.clear_color.Value.z, 1.0);
1117 glClear(GL_COLOR_BUFFER_BIT);
1118 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1119 }
1120
1121 static void
1122 realize_area(GtkGLArea *area)
1123 {
1124 ImGui::CreateContext();
1125 ImGui_ImplGtk3_Init(GTK_WIDGET(area), true);
1126 ImGui_ImplOpenGL3_Init("#version 130");
1127
1128 list_inithead(&context.windows);
1129
1130 ImGui::StyleColorsDark();
1131 context.cfg = aub_viewer_cfg();
1132
1133 ImGuiIO& io = ImGui::GetIO();
1134 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
1135 }
1136
1137 static void
1138 unrealize_area(GtkGLArea *area)
1139 {
1140 gtk_gl_area_make_current(area);
1141
1142 ImGui_ImplOpenGL3_Shutdown();
1143 ImGui_ImplGtk3_Shutdown();
1144 ImGui::DestroyContext();
1145 }
1146
1147 static void
1148 size_allocate_area(GtkGLArea *area,
1149 GdkRectangle *allocation,
1150 gpointer user_data)
1151 {
1152 if (!gtk_widget_get_realized(GTK_WIDGET(area)))
1153 return;
1154
1155 /* We want to catch only initial size allocate. */
1156 g_signal_handlers_disconnect_by_func(area,
1157 (gpointer) size_allocate_area,
1158 user_data);
1159 show_aubfile_window();
1160 }
1161
1162 static void
1163 print_help(const char *progname, FILE *file)
1164 {
1165 fprintf(file,
1166 "Usage: %s [OPTION]... FILE\n"
1167 "Decode aub file contents from FILE.\n\n"
1168 " --help display this help and exit\n"
1169 " -x, --xml=DIR load hardware xml description from directory DIR\n",
1170 progname);
1171 }
1172
1173 int main(int argc, char *argv[])
1174 {
1175 int c, i;
1176 bool help = false;
1177 const struct option aubinator_opts[] = {
1178 { "help", no_argument, (int *) &help, true },
1179 { "xml", required_argument, NULL, 'x' },
1180 { NULL, 0, NULL, 0 }
1181 };
1182
1183 memset(&context, 0, sizeof(context));
1184
1185 i = 0;
1186 while ((c = getopt_long(argc, argv, "x:s:", aubinator_opts, &i)) != -1) {
1187 switch (c) {
1188 case 'x':
1189 context.xml_path = strdup(optarg);
1190 break;
1191 default:
1192 break;
1193 }
1194 }
1195
1196 if (optind < argc)
1197 context.input_file = argv[optind];
1198
1199 if (help || !context.input_file) {
1200 print_help(argv[0], stderr);
1201 exit(0);
1202 }
1203
1204 context.file = aub_file_open(context.input_file);
1205
1206 gtk_init(NULL, NULL);
1207
1208 context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1209 gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Aubinator Viewer");
1210 g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
1211 gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720);
1212
1213 GtkWidget* gl_area = gtk_gl_area_new();
1214 g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL);
1215 g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL);
1216 g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL);
1217 g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);
1218 gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
1219
1220 gtk_widget_show_all(context.gtk_window);
1221
1222 gtk_main();
1223
1224 free(context.xml_path);
1225
1226 return EXIT_SUCCESS;
1227 }