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