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