• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 brw_isa_info isa;
66    struct intel_spec *spec;
67 };
68 
69 static void
store_exec_begin(struct aub_file * file)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
store_exec_end(struct aub_file * file)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
handle_mem_write(void * user_data,uint64_t phys_addr,const void * data,uint32_t data_len)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
handle_ring_write(void * user_data,enum drm_i915_gem_engine_class engine,const void * ring_data,uint32_t ring_data_len)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
handle_reg_write(void * user_data,uint32_t reg_offset,uint32_t reg_value)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
handle_info(void * user_data,int pci_id,const char * app_name)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 (!intel_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    brw_init_isa_info(&file->isa, &file->devinfo);
134    file->spec = intel_spec_load(&file->devinfo);
135 }
136 
137 static void
handle_error(void * user_data,const void * aub_data,const char * msg)138 handle_error(void *user_data, const void *aub_data, const char *msg)
139 {
140    fprintf(stderr, "ERROR: %s", msg);
141 }
142 
143 static struct aub_file *
aub_file_open(const char * filename)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
update_mem_for_exec(struct aub_mem * mem,struct aub_file * file,int exec_idx)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 intel_batch_decode_bo aub_bo;
256    uint64_t aub_offset;
257 
258    struct intel_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
map_key(int k)329 map_key(int k)
330 {
331    return ImGuiKey_COUNT + k;
332 }
333 
334 static bool
has_ctrl_key(int key)335 has_ctrl_key(int key)
336 {
337    return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));
338 }
339 
340 static bool
window_has_ctrl_key(int key)341 window_has_ctrl_key(int key)
342 {
343    return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
344 }
345 
346 static void
destroy_window_noop(struct window * win)347 destroy_window_noop(struct window *win)
348 {
349 }
350 
351 /* Shader windows */
352 
353 static void
display_shader_window(struct window * win)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
destroy_shader_window(struct window * win)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 *
new_shader_window(struct aub_mem * mem,uint64_t address,const char * desc)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 intel_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          intel_disassemble(&context.file->isa,
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
display_urb_window(struct window * win)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
destroy_urb_window(struct window * win)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 *
new_urb_window(struct aub_viewer_decode_ctx * decode_ctx,uint64_t address)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
read_edit_window(const uint8_t * data,size_t off)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
write_edit_window(uint8_t * data,size_t off,uint8_t d)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
display_edit_window(struct window * win)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
destroy_edit_window(struct window * win)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 *
new_edit_window(struct aub_mem * mem,uint64_t address,uint32_t len)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
display_pml4_level(struct aub_mem * mem,uint64_t table_addr,uint64_t table_virt_addr,int level)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 intel_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
display_pml4_window(struct window * win)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
show_pml4_window(struct pml4_window * window,struct aub_mem * mem)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
display_decode_options(struct aub_viewer_decode_cfg * cfg)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
batch_display_shader(void * user_data,const char * shader_desc,uint64_t address)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
batch_display_urb(void * user_data,const struct aub_decode_urb_stage_state * stages)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
batch_edit_address(void * user_data,uint64_t address,uint32_t len)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 intel_batch_decode_bo
batch_get_bo(void * user_data,bool ppgtt,uint64_t address)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
update_batch_window(struct batch_window * window,bool reset,int exec_idx)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
display_batch_ring_write(void * user_data,enum drm_i915_gem_engine_class engine,const void * data,uint32_t data_len)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
display_batch_execlist_write(void * user_data,enum drm_i915_gem_engine_class engine,uint64_t context_descriptor)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 intel_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 intel_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
display_batch_window(struct window * win)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
destroy_batch_window(struct window * win)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
new_batch_window(int exec_idx)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->devinfo,
822                               context.file->spec,
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
display_registers_window(struct window * win)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 intel_group *reg = (struct intel_group *) entry->data;
847       if (filter.PassFilter(reg->name) &&
848           ImGui::CollapsingHeader(reg->name)) {
849          const struct intel_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
show_register_window(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
display_commands_window(struct window * win)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 intel_group *cmd = (struct intel_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 intel_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 intel_group *cmd = (struct intel_group *) entry->data;
924       if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
925           ImGui::CollapsingHeader(cmd->name)) {
926          const struct intel_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
show_commands_window(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 *
human_size(size_t size)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
display_aubfile_window(struct window * win)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", context.file->devinfo.name);
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
show_aubfile_window(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
display_windows(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
repaint_area(GtkGLArea * area,GdkGLContext * gdk_gl_context)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
realize_area(GtkGLArea * area)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
unrealize_area(GtkGLArea * area)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
size_allocate_area(GtkGLArea * area,GdkRectangle * allocation,gpointer user_data)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
print_help(const char * progname,FILE * file)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 
main(int argc,char * argv[])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 }
1228