summaryrefslogtreecommitdiffstats
path: root/src/intel/tools/aubinator_viewer.cpp
diff options
context:
space:
mode:
authorLionel Landwerlin <[email protected]>2018-07-28 19:13:28 +0100
committerLionel Landwerlin <[email protected]>2018-08-22 18:02:11 +0100
commit38f10d5a03542c60a589ff6a347df86790de00b7 (patch)
tree4094129182c1d822dd7439fb2b2c46171651fcff /src/intel/tools/aubinator_viewer.cpp
parentea83a1d304dc97d1d155a633e95d902a08703175 (diff)
intel: tools: add aubinator viewer
A graphical user interface version of aubinator. Allows you to : - simultaneously look at multiple points in the aub file (using all the goodness of the existing decoding in aubinator) - edit an aub file v2: Switch from GLFW to GTK+3 v3: Fix warning when exiting Signed-off-by: Lionel Landwerlin <[email protected]> Acked-by: Rafael Antognolli <[email protected]> (v1)
Diffstat (limited to 'src/intel/tools/aubinator_viewer.cpp')
-rw-r--r--src/intel/tools/aubinator_viewer.cpp1137
1 files changed, 1137 insertions, 0 deletions
diff --git a/src/intel/tools/aubinator_viewer.cpp b/src/intel/tools/aubinator_viewer.cpp
new file mode 100644
index 00000000000..7ef56aa035f
--- /dev/null
+++ b/src/intel/tools/aubinator_viewer.cpp
@@ -0,0 +1,1137 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "util/macros.h"
+
+#include "aub_read.h"
+#include "aub_mem.h"
+
+#include "common/gen_disasm.h"
+
+#define xtzalloc(name) ((decltype(&name)) calloc(1, sizeof(name)))
+#define xtalloc(name) ((decltype(&name)) malloc(sizeof(name)))
+
+struct aub_file {
+ uint8_t *map, *end, *cursor;
+
+ uint16_t pci_id;
+ char app_name[33];
+
+ /* List of batch buffers to process */
+ struct {
+ const uint8_t *start;
+ const uint8_t *end;
+ } *execs;
+ int n_execs;
+ int n_allocated_execs;
+
+ uint32_t idx_reg_write;
+
+ /* Device state */
+ struct gen_device_info devinfo;
+ struct gen_spec *spec;
+ struct gen_disasm *disasm;
+};
+
+static void
+store_exec_begin(struct aub_file *file)
+{
+ if (unlikely(file->n_execs >= file->n_allocated_execs)) {
+ file->n_allocated_execs = MAX2(2U * file->n_allocated_execs,
+ 4096 / sizeof(file->execs[0]));
+ file->execs = (decltype(file->execs))
+ realloc(static_cast<void *>(file->execs),
+ file->n_allocated_execs * sizeof(file->execs[0]));
+ }
+
+ file->execs[file->n_execs++].start = file->cursor;
+}
+
+static void
+store_exec_end(struct aub_file *file)
+{
+ if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)
+ file->execs[file->n_execs - 1].end = file->cursor;
+}
+
+static void
+handle_mem_write(void *user_data, uint64_t phys_addr,
+ const void *data, uint32_t data_len)
+{
+ struct aub_file *file = (struct aub_file *) user_data;
+ file->idx_reg_write = 0;
+ store_exec_end(file);
+}
+
+static void
+handle_ring_write(void *user_data, enum gen_engine engine,
+ const void *ring_data, uint32_t ring_data_len)
+{
+ struct aub_file *file = (struct aub_file *) user_data;
+ file->idx_reg_write = 0;
+ store_exec_begin(file);
+}
+
+static void
+handle_reg_write(void *user_data, uint32_t reg_offset, uint32_t reg_value)
+{
+ struct aub_file *file = (struct aub_file *) user_data;
+
+ /* Only store the first register write of a series (execlist writes take
+ * involve 2 dwords).
+ */
+ if (file->idx_reg_write++ == 0)
+ store_exec_begin(file);
+}
+
+static void
+handle_info(void *user_data, int pci_id, const char *app_name)
+{
+ struct aub_file *file = (struct aub_file *) user_data;
+ store_exec_end(file);
+
+ file->pci_id = pci_id;
+ snprintf(file->app_name, sizeof(app_name), "%s", app_name);
+
+ if (!gen_get_device_info(file->pci_id, &file->devinfo)) {
+ fprintf(stderr, "can't find device information: pci_id=0x%x\n", file->pci_id);
+ exit(EXIT_FAILURE);
+ }
+ file->spec = gen_spec_load(&file->devinfo);
+ file->disasm = gen_disasm_create(&file->devinfo);
+}
+
+static void
+handle_error(void *user_data, const void *aub_data, const char *msg)
+{
+ fprintf(stderr, "ERROR: %s\n", msg);
+}
+
+static struct aub_file *
+aub_file_open(const char *filename)
+{
+ struct aub_file *file;
+ struct stat sb;
+ int fd;
+
+ file = xtzalloc(*file);
+ fd = open(filename, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (fstat(fd, &sb) == -1) {
+ fprintf(stderr, "stat failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ file->map = (uint8_t *) mmap(NULL, sb.st_size,
+ PROT_READ, MAP_SHARED, fd, 0);
+ if (file->map == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ file->cursor = file->map;
+ file->end = file->map + sb.st_size;
+
+ struct aub_read aub_read = {};
+ aub_read.user_data = file;
+ aub_read.info = handle_info;
+ aub_read.error = handle_error;
+ aub_read.reg_write = handle_reg_write;
+ aub_read.ring_write = handle_ring_write;
+ aub_read.local_write = handle_mem_write;
+ aub_read.phys_write = handle_mem_write;
+ aub_read.ggtt_write = handle_mem_write;
+ aub_read.ggtt_entry_write = handle_mem_write;
+
+ int consumed;
+ while (file->cursor < file->end &&
+ (consumed = aub_read_command(&aub_read, file->cursor,
+ file->end - file->cursor)) > 0) {
+ file->cursor += consumed;
+ }
+
+ /* Ensure we have an end on the last register write. */
+ if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)
+ file->execs[file->n_execs - 1].end = file->end;
+
+ return file;
+}
+
+/**/
+
+static void
+update_mem_for_exec(struct aub_mem *mem, struct aub_file *file, int exec_idx)
+{
+ struct aub_read read = {};
+ read.user_data = mem;
+ read.local_write = aub_mem_local_write;
+ read.phys_write = aub_mem_phys_write;
+ read.ggtt_write = aub_mem_ggtt_write;
+ read.ggtt_entry_write = aub_mem_ggtt_entry_write;
+
+ /* Replay the aub file from the beginning up to just before the
+ * commands we want to read. where the context setup happens.
+ */
+ const uint8_t *iter = file->map;
+ while (iter < file->execs[exec_idx].start) {
+ iter += aub_read_command(&read, iter, file->execs[exec_idx].start - iter);
+ }
+}
+
+/* UI */
+
+#include <epoxy/gl.h>
+
+#include "imgui.h"
+#include "imgui_impl_gtk3.h"
+#include "imgui_impl_opengl3.h"
+
+#include "aubinator_viewer.h"
+#include "imgui_memory_editor.h"
+
+struct window {
+ struct list_head link; /* link in the global list of windows */
+ struct list_head parent_link; /* link in parent window list of children */
+
+ struct list_head children_windows; /* list of children windows */
+
+ char name[128];
+ bool opened;
+
+ ImVec2 position;
+ ImVec2 size;
+
+ void (*display)(struct window*);
+ void (*destroy)(struct window*);
+};
+
+struct edit_window {
+ struct window base;
+
+ struct aub_mem *mem;
+ uint64_t address;
+ uint32_t len;
+
+ struct gen_batch_decode_bo aub_bo;
+ uint64_t aub_offset;
+
+ struct gen_batch_decode_bo gtt_bo;
+ uint64_t gtt_offset;
+
+ struct MemoryEditor editor;
+};
+
+struct pml4_window {
+ struct window base;
+
+ struct aub_mem *mem;
+};
+
+struct shader_window {
+ struct window base;
+
+ uint64_t address;
+ char *shader;
+ size_t shader_size;
+};
+
+struct batch_window {
+ struct window base;
+
+ struct aub_mem mem;
+ struct aub_read read;
+
+ bool uses_ppgtt;
+
+ bool collapsed;
+ int exec_idx;
+
+ struct aub_viewer_decode_cfg decode_cfg;
+ struct aub_viewer_decode_ctx decode_ctx;
+
+ struct pml4_window pml4_window;
+
+ char edit_address[20];
+};
+
+static struct Context {
+ struct aub_file *file;
+ char *input_file;
+ char *xml_path;
+
+ GtkWidget *gtk_window;
+
+ /* UI state*/
+ bool show_commands_window;
+ bool show_registers_window;
+
+ struct aub_viewer_cfg cfg;
+
+ struct list_head windows;
+
+ struct window file_window;
+ struct window commands_window;
+ struct window registers_window;
+} context;
+
+static int
+map_key(int k)
+{
+ return ImGuiKey_COUNT + k;
+}
+
+static bool
+has_ctrl_key(int key)
+{
+ return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));
+}
+
+static bool
+window_has_ctrl_key(int key)
+{
+ return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
+}
+
+static void
+destroy_window_noop(struct window *win)
+{
+}
+
+/* Shader windows */
+
+static void
+display_shader_window(struct window *win)
+{
+ struct shader_window *window = (struct shader_window *) win;
+
+ if (window->shader) {
+ ImGui::InputTextMultiline("Assembly",
+ window->shader, window->shader_size,
+ ImGui::GetContentRegionAvail(),
+ ImGuiInputTextFlags_ReadOnly);
+ } else {
+ ImGui::Text("Shader not available");
+ }
+}
+
+static void
+destroy_shader_window(struct window *win)
+{
+ struct shader_window *window = (struct shader_window *) win;
+
+ free(window->shader);
+ free(window);
+}
+
+static struct shader_window *
+new_shader_window(struct aub_mem *mem, uint64_t address, const char *desc)
+{
+ struct shader_window *window = xtzalloc(*window);
+
+ snprintf(window->base.name, sizeof(window->base.name),
+ "%s (0x%lx)##%p", desc, address, window);
+
+ list_inithead(&window->base.parent_link);
+ window->base.position = ImVec2(-1, -1);
+ window->base.size = ImVec2(700, 300);
+ window->base.opened = true;
+ window->base.display = display_shader_window;
+ window->base.destroy = destroy_shader_window;
+
+ struct gen_batch_decode_bo shader_bo;
+ if (mem->pml4)
+ shader_bo = aub_mem_get_ppgtt_bo(mem, address);
+ else
+ shader_bo = aub_mem_get_ggtt_bo(mem, address);
+
+ if (shader_bo.map) {
+ FILE *f = open_memstream(&window->shader, &window->shader_size);
+ if (f) {
+ gen_disasm_disassemble(context.file->disasm, shader_bo.map, 0, f);
+ fclose(f);
+ }
+ }
+
+ list_addtail(&window->base.link, &context.windows);
+
+ return window;
+}
+
+/* Memory editor windows */
+
+static uint8_t
+read_edit_window(const uint8_t *data, size_t off)
+{
+ struct edit_window *window = (struct edit_window *) data;
+
+ return *((const uint8_t *) window->gtt_bo.map + window->gtt_offset + off);
+}
+
+static void
+write_edit_window(uint8_t *data, size_t off, uint8_t d)
+{
+ struct edit_window *window = (struct edit_window *) data;
+ uint8_t *gtt = (uint8_t *) window->gtt_bo.map + window->gtt_offset + off;
+ uint8_t *aub = (uint8_t *) window->aub_bo.map + window->aub_offset + off;
+
+ *gtt = *aub = d;
+}
+
+static void
+display_edit_window(struct window *win)
+{
+ struct edit_window *window = (struct edit_window *) win;
+
+ if (window->aub_bo.map && window->gtt_bo.map) {
+ ImGui::BeginChild(ImGui::GetID("##block"));
+ window->editor.DrawContents((uint8_t *) window,
+ MIN3(window->len,
+ window->gtt_bo.size - window->gtt_offset,
+ window->aub_bo.size - window->aub_offset),
+ window->address);
+ ImGui::EndChild();
+ } else {
+ ImGui::Text("Memory view at 0x%lx not available", window->address);
+ }
+}
+
+static void
+destroy_edit_window(struct window *win)
+{
+ struct edit_window *window = (struct edit_window *) win;
+
+ if (window->aub_bo.map)
+ mprotect((void *) window->aub_bo.map, 4096, PROT_READ);
+ free(window);
+}
+
+static struct edit_window *
+new_edit_window(struct aub_mem *mem, uint64_t address, uint32_t len)
+{
+ struct edit_window *window = xtzalloc(*window);
+
+ snprintf(window->base.name, sizeof(window->base.name),
+ "Editing aub at 0x%lx##%p", address, window);
+
+ list_inithead(&window->base.parent_link);
+ window->base.position = ImVec2(-1, -1);
+ window->base.size = ImVec2(500, 600);
+ window->base.opened = true;
+ window->base.display = display_edit_window;
+ window->base.destroy = destroy_edit_window;
+
+ window->mem = mem;
+ window->address = address;
+ window->aub_bo = aub_mem_get_ppgtt_addr_aub_data(mem, address);
+ window->gtt_bo = aub_mem_get_ppgtt_addr_data(mem, address);
+ window->len = len;
+ window->editor = MemoryEditor();
+ window->editor.OptShowDataPreview = true;
+ window->editor.OptShowAscii = false;
+ window->editor.ReadFn = read_edit_window;
+ window->editor.WriteFn = write_edit_window;
+
+ if (window->aub_bo.map) {
+ uint64_t unaligned_map = (uint64_t) window->aub_bo.map;
+ window->aub_bo.map = (const void *)(unaligned_map & ~0xffful);
+ window->aub_offset = unaligned_map - (uint64_t) window->aub_bo.map;
+
+ if (mprotect((void *) window->aub_bo.map, window->aub_bo.size, PROT_READ | PROT_WRITE) != 0) {
+ window->aub_bo.map = NULL;
+ }
+ }
+
+ window->gtt_offset = address - window->gtt_bo.addr;
+
+ list_addtail(&window->base.link, &context.windows);
+
+ return window;
+}
+
+/* 4 level page table walk windows */
+
+static void
+display_pml4_level(struct aub_mem *mem, uint64_t table_addr, uint64_t table_virt_addr, int level)
+{
+ if (level == 0)
+ return;
+
+ struct gen_batch_decode_bo table_bo =
+ aub_mem_get_phys_addr_data(mem, table_addr);
+ const uint64_t *table = (const uint64_t *) ((const uint8_t *) table_bo.map +
+ table_addr - table_bo.addr);
+ if (!table) {
+ ImGui::TextColored(context.cfg.missing_color, "Page not available");
+ return;
+ }
+
+ uint64_t addr_increment = 1ULL << (12 + 9 * (level - 1));
+
+ if (level == 1) {
+ for (int e = 0; e < 512; e++) {
+ bool available = (table[e] & 1) != 0;
+ uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
+ if (!available)
+ continue;
+ ImGui::Text("Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx",
+ e, table[e], entry_virt_addr);
+ }
+ } else {
+ for (int e = 0; e < 512; e++) {
+ bool available = (table[e] & 1) != 0;
+ uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;
+ if (available &&
+ ImGui::TreeNodeEx(&table[e],
+ available ? ImGuiTreeNodeFlags_Framed : 0,
+ "Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx",
+ e, table[e], entry_virt_addr)) {
+ display_pml4_level(mem, table[e] & ~0xffful, entry_virt_addr, level -1);
+ ImGui::TreePop();
+ }
+ }
+ }
+}
+
+static void
+display_pml4_window(struct window *win)
+{
+ struct pml4_window *window = (struct pml4_window *) win;
+
+ ImGui::Text("pml4: %lx", window->mem->pml4);
+ ImGui::BeginChild(ImGui::GetID("##block"));
+ display_pml4_level(window->mem, window->mem->pml4, 0, 4);
+ ImGui::EndChild();
+}
+
+static void
+show_pml4_window(struct pml4_window *window, struct aub_mem *mem)
+{
+ if (window->base.opened) {
+ window->base.opened = false;
+ return;
+ }
+
+ snprintf(window->base.name, sizeof(window->base.name),
+ "4-Level page tables##%p", window);
+
+ list_inithead(&window->base.parent_link);
+ window->base.position = ImVec2(-1, -1);
+ window->base.size = ImVec2(500, 600);
+ window->base.opened = true;
+ window->base.display = display_pml4_window;
+ window->base.destroy = destroy_window_noop;
+
+ window->mem = mem;
+
+ list_addtail(&window->base.link, &context.windows);
+}
+
+/* Batch decoding windows */
+
+static void
+display_decode_options(struct aub_viewer_decode_cfg *cfg)
+{
+ char name[40];
+ snprintf(name, sizeof(name), "command filter##%p", &cfg->command_filter);
+ cfg->command_filter.Draw(name); ImGui::SameLine();
+ snprintf(name, sizeof(name), "field filter##%p", &cfg->field_filter);
+ cfg->field_filter.Draw(name); ImGui::SameLine();
+ if (ImGui::Button("Dwords")) cfg->show_dwords ^= 1;
+}
+
+static void
+batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)
+{
+ struct batch_window *window = (struct batch_window *) user_data;
+ struct shader_window *shader_window =
+ new_shader_window(&window->mem, address, shader_desc);
+
+ list_add(&shader_window->base.parent_link, &window->base.children_windows);
+}
+
+static void
+batch_edit_address(void *user_data, uint64_t address, uint32_t len)
+{
+ struct batch_window *window = (struct batch_window *) user_data;
+ struct edit_window *edit_window =
+ new_edit_window(&window->mem, address, len);
+
+ list_add(&edit_window->base.parent_link, &window->base.children_windows);
+}
+
+static struct gen_batch_decode_bo
+batch_get_bo(void *user_data, uint64_t address)
+{
+ struct batch_window *window = (struct batch_window *) user_data;
+
+ if (window->uses_ppgtt)
+ return aub_mem_get_ppgtt_bo(&window->mem, address);
+ else
+ return aub_mem_get_ggtt_bo(&window->mem, address);
+}
+
+static void
+update_batch_window(struct batch_window *window, bool reset, int exec_idx)
+{
+ if (reset)
+ aub_mem_fini(&window->mem);
+ aub_mem_init(&window->mem);
+
+ window->exec_idx = MAX2(MIN2(context.file->n_execs - 1, exec_idx), 0);
+ update_mem_for_exec(&window->mem, context.file, window->exec_idx);
+}
+
+static void
+display_batch_ring_write(void *user_data, enum gen_engine engine,
+ const void *data, uint32_t data_len)
+{
+ struct batch_window *window = (struct batch_window *) user_data;
+
+ window->uses_ppgtt = false;
+
+ aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0);
+}
+
+static void
+display_batch_execlist_write(void *user_data, enum gen_engine engine,
+ uint64_t context_descriptor)
+{
+ struct batch_window *window = (struct batch_window *) user_data;
+
+ const uint32_t pphwsp_size = 4096;
+ uint32_t pphwsp_addr = context_descriptor & 0xfffff000;
+ struct gen_batch_decode_bo pphwsp_bo =
+ aub_mem_get_ggtt_bo(&window->mem, pphwsp_addr);
+ uint32_t *context_img = (uint32_t *)((uint8_t *)pphwsp_bo.map +
+ (pphwsp_addr - pphwsp_bo.addr) +
+ pphwsp_size);
+
+ uint32_t ring_buffer_head = context_img[5];
+ uint32_t ring_buffer_tail = context_img[7];
+ uint32_t ring_buffer_start = context_img[9];
+
+ window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51];
+
+ struct gen_batch_decode_bo ring_bo =
+ aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start);
+ assert(ring_bo.size > 0);
+ void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr);
+
+ window->uses_ppgtt = true;
+
+ aub_viewer_render_batch(&window->decode_ctx, commands,
+ ring_buffer_tail - ring_buffer_head,
+ ring_buffer_start);
+}
+
+static void
+display_batch_window(struct window *win)
+{
+ struct batch_window *window = (struct batch_window *) win;
+
+ ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
+ if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
+ display_decode_options(&window->decode_cfg);
+ ImGui::PopItemWidth();
+
+ if (ImGui::InputInt("Execbuf", &window->exec_idx))
+ update_batch_window(window, true, window->exec_idx);
+
+ if (window_has_ctrl_key('p'))
+ update_batch_window(window, true, window->exec_idx - 1);
+ if (window_has_ctrl_key('n'))
+ update_batch_window(window, true, window->exec_idx + 1);
+
+ ImGui::Text("execbuf %i", window->exec_idx);
+ if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window->pml4_window, &window->mem); }
+
+ ImGui::BeginChild(ImGui::GetID("##block"));
+
+ struct aub_read read = {};
+ read.user_data = window;
+ read.ring_write = display_batch_ring_write;
+ read.execlist_write = display_batch_execlist_write;
+
+ const uint8_t *iter = context.file->execs[window->exec_idx].start;
+ while (iter < context.file->execs[window->exec_idx].end) {
+ iter += aub_read_command(&read, iter,
+ context.file->execs[window->exec_idx].end - iter);
+ }
+
+ ImGui::EndChild();
+}
+
+static void
+destroy_batch_window(struct window *win)
+{
+ struct batch_window *window = (struct batch_window *) win;
+
+ aub_mem_fini(&window->mem);
+
+ /* This works because children windows are inserted at the back of the
+ * list, ensuring the deletion loop goes through the children after calling
+ * this function.
+ */
+ list_for_each_entry(struct window, child_window,
+ &window->base.children_windows, parent_link)
+ child_window->opened = false;
+ window->pml4_window.base.opened = false;
+
+ free(window);
+}
+
+static void
+new_batch_window(int exec_idx)
+{
+ struct batch_window *window = xtzalloc(*window);
+
+ snprintf(window->base.name, sizeof(window->base.name),
+ "Batch view##%p", window);
+
+ list_inithead(&window->base.parent_link);
+ list_inithead(&window->base.children_windows);
+ window->base.position = ImVec2(-1, -1);
+ window->base.size = ImVec2(600, 700);
+ window->base.opened = true;
+ window->base.display = display_batch_window;
+ window->base.destroy = destroy_batch_window;
+
+ window->collapsed = true;
+ window->decode_cfg = aub_viewer_decode_cfg();
+
+ aub_viewer_decode_ctx_init(&window->decode_ctx,
+ &context.cfg,
+ &window->decode_cfg,
+ context.file->spec,
+ context.file->disasm,
+ batch_get_bo,
+ NULL,
+ window);
+ window->decode_ctx.display_shader = batch_display_shader;
+ window->decode_ctx.edit_address = batch_edit_address;
+
+ update_batch_window(window, false, exec_idx);
+
+ list_addtail(&window->base.link, &context.windows);
+}
+
+/**/
+
+static void
+display_registers_window(struct window *win)
+{
+ static struct ImGuiTextFilter filter;
+ if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
+ filter.Draw();
+
+ ImGui::BeginChild(ImGui::GetID("##block"));
+ struct hash_entry *entry;
+ hash_table_foreach(context.file->spec->registers_by_name, entry) {
+ struct gen_group *reg = (struct gen_group *) entry->data;
+ if (filter.PassFilter(reg->name) &&
+ ImGui::CollapsingHeader(reg->name)) {
+ const struct gen_field *field = reg->fields;
+ while (field) {
+ ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
+ field = field->next;
+ }
+ }
+ }
+ ImGui::EndChild();
+}
+
+static void
+show_register_window(void)
+{
+ struct window *window = &context.registers_window;
+
+ if (window->opened) {
+ window->opened = false;
+ return;
+ }
+
+ snprintf(window->name, sizeof(window->name), "Registers");
+
+ list_inithead(&window->parent_link);
+ window->position = ImVec2(-1, -1);
+ window->size = ImVec2(200, 400);
+ window->opened = true;
+ window->display = display_registers_window;
+ window->destroy = destroy_window_noop;
+
+ list_addtail(&window->link, &context.windows);
+}
+
+static void
+display_commands_window(struct window *win)
+{
+ static struct ImGuiTextFilter cmd_filter;
+ if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
+ cmd_filter.Draw("name filter");
+ static struct ImGuiTextFilter field_filter;
+ field_filter.Draw("field filter");
+
+ static char opcode_str[9] = { 0, };
+ ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str),
+ ImGuiInputTextFlags_CharsHexadecimal);
+ size_t opcode_len = strlen(opcode_str);
+ uint64_t opcode = strtol(opcode_str, NULL, 16);
+
+ static bool show_dwords = true;
+ if (ImGui::Button("Dwords")) show_dwords ^= 1;
+
+ ImGui::BeginChild(ImGui::GetID("##block"));
+ struct hash_entry *entry;
+ hash_table_foreach(context.file->spec->commands, entry) {
+ struct gen_group *cmd = (struct gen_group *) entry->data;
+ if ((cmd_filter.PassFilter(cmd->name) &&
+ (opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&
+ ImGui::CollapsingHeader(cmd->name)) {
+ const struct gen_field *field = cmd->fields;
+ int32_t last_dword = -1;
+ while (field) {
+ if (show_dwords && field->start / 32 != last_dword) {
+ for (last_dword = MAX2(0, last_dword + 1);
+ last_dword < field->start / 32; last_dword++) {
+ ImGui::TextColored(context.cfg.dwords_color,
+ "Dword %d", last_dword);
+ }
+ ImGui::TextColored(context.cfg.dwords_color, "Dword %d", last_dword);
+ }
+ if (field_filter.PassFilter(field->name))
+ ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
+ field = field->next;
+ }
+ }
+ }
+ hash_table_foreach(context.file->spec->structs, entry) {
+ struct gen_group *cmd = (struct gen_group *) entry->data;
+ if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
+ ImGui::CollapsingHeader(cmd->name)) {
+ const struct gen_field *field = cmd->fields;
+ int32_t last_dword = -1;
+ while (field) {
+ if (show_dwords && field->start / 32 != last_dword) {
+ last_dword = field->start / 32;
+ ImGui::TextColored(context.cfg.dwords_color,
+ "Dword %d", last_dword);
+ }
+ if (field_filter.PassFilter(field->name))
+ ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);
+ field = field->next;
+ }
+ }
+ }
+ ImGui::EndChild();
+}
+
+static void
+show_commands_window(void)
+{
+ struct window *window = &context.commands_window;
+
+ if (window->opened) {
+ window->opened = false;
+ return;
+ }
+
+ snprintf(window->name, sizeof(window->name), "Commands & structs");
+
+ list_inithead(&window->parent_link);
+ window->position = ImVec2(-1, -1);
+ window->size = ImVec2(300, 400);
+ window->opened = true;
+ window->display = display_commands_window;
+ window->destroy = destroy_window_noop;
+
+ list_addtail(&window->link, &context.windows);
+}
+
+/* Main window */
+
+static const char *
+human_size(size_t size)
+{
+ unsigned divisions = 0;
+ double v = size;
+ double divider = 1024;
+ while (v >= divider) {
+ v /= divider;
+ divisions++;
+ }
+
+ static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
+ static char result[20];
+ snprintf(result, sizeof(result), "%.2f %s",
+ v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);
+ return result;
+}
+
+static void
+display_aubfile_window(struct window *win)
+{
+ ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |
+ ImGuiColorEditFlags_NoLabel |
+ ImGuiColorEditFlags_NoInputs);
+ struct aub_viewer_cfg *cfg = &context.cfg;
+
+ ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine();
+ ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine();
+ ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine();
+ ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine();
+ ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine();
+
+ if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
+ if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
+ if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
+
+ if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
+
+ ImGui::Text("File name: %s", context.input_file);
+ ImGui::Text("File size: %s", human_size(context.file->end - context.file->map));
+ ImGui::Text("Execbufs %u", context.file->n_execs);
+ ImGui::Text("PCI ID: 0x%x", context.file->pci_id);
+ ImGui::Text("Application name: %s", context.file->app_name);
+ ImGui::Text(gen_get_device_name(context.file->pci_id));
+
+ ImGui::SetNextWindowContentWidth(500);
+ if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
+ ImGui::Text("Some global keybindings:");
+ ImGui::Separator();
+
+ static const char *texts[] = {
+ "Ctrl-h", "show this screen",
+ "Ctrl-c", "show commands list",
+ "Ctrl-r", "show registers list",
+ "Ctrl-b", "new batch window",
+ "Ctrl-p/n", "switch to previous/next batch buffer",
+ "Ctrl-Tab", "switch focus between window",
+ "Ctrl-left/right", "align window to the side of the screen",
+ };
+ float align = 0.0f;
+ for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)
+ align = MAX2(align, ImGui::CalcTextSize(texts[i]).x);
+ align += ImGui::GetStyle().WindowPadding.x + 10;
+
+ for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {
+ ImGui::Text(texts[i]); ImGui::SameLine(align); ImGui::Text(texts[i + 1]);
+ }
+
+ if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
+ ImGui::CloseCurrentPopup();
+ ImGui::EndPopup();
+ }
+}
+
+static void
+show_aubfile_window(void)
+{
+ struct window *window = &context.file_window;
+
+ if (window->opened)
+ return;
+
+ snprintf(window->name, sizeof(window->name),
+ "Aubinator Viewer: Intel AUB file decoder/editor");
+
+ list_inithead(&window->parent_link);
+ window->size = ImVec2(-1, 250);
+ window->position =
+ ImVec2(0, ImGui::GetIO().DisplaySize.y - window->size.y);
+ window->opened = true;
+ window->display = display_aubfile_window;
+ window->destroy = NULL;
+
+ list_addtail(&window->link, &context.windows);
+}
+
+/* Main redrawing */
+
+static void
+display_windows(void)
+{
+ /* Start by disposing closed windows, we don't want to destroy windows that
+ * have already been scheduled to be painted. So destroy always happens on
+ * the next draw cycle, prior to any drawing.
+ */
+ list_for_each_entry_safe(struct window, window, &context.windows, link) {
+ if (window->opened)
+ continue;
+
+ /* Can't close this one. */
+ if (window == &context.file_window) {
+ window->opened = true;
+ continue;
+ }
+
+ list_del(&window->link);
+ list_del(&window->parent_link);
+ if (window->destroy)
+ window->destroy(window);
+ }
+
+ list_for_each_entry(struct window, window, &context.windows, link) {
+ ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver);
+ if (ImGui::Begin(window->name, &window->opened)) {
+ window->display(window);
+ window->position = ImGui::GetWindowPos();
+ window->size = ImGui::GetWindowSize();
+ }
+ if (window_has_ctrl_key('w'))
+ window->opened = false;
+ ImGui::End();
+ }
+}
+
+static void
+repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context)
+{
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGtk3_NewFrame();
+ ImGui::NewFrame();
+
+ display_windows();
+
+ ImGui::EndFrame();
+ ImGui::Render();
+
+ glClearColor(context.cfg.clear_color.Value.x,
+ context.cfg.clear_color.Value.y,
+ context.cfg.clear_color.Value.z, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+}
+
+static void
+realize_area(GtkGLArea *area)
+{
+ ImGui::CreateContext();
+ ImGui_ImplGtk3_Init(GTK_WIDGET(area), true);
+ ImGui_ImplOpenGL3_Init("#version 130");
+
+ list_inithead(&context.windows);
+
+ ImGui::StyleColorsDark();
+ context.cfg = aub_viewer_cfg();
+
+ ImGuiIO& io = ImGui::GetIO();
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+}
+
+static void
+unrealize_area(GtkGLArea *area)
+{
+ gtk_gl_area_make_current(area);
+
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGtk3_Shutdown();
+ ImGui::DestroyContext();
+}
+
+static void
+print_help(const char *progname, FILE *file)
+{
+ fprintf(file,
+ "Usage: %s [OPTION]... FILE\n"
+ "Decode aub file contents from FILE.\n\n"
+ " --help display this help and exit\n"
+ " -x, --xml=DIR load hardware xml description from directory DIR\n",
+ progname);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, i;
+ bool help = false;
+ const struct option aubinator_opts[] = {
+ { "help", no_argument, (int *) &help, true },
+ { "xml", required_argument, NULL, 'x' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ memset(&context, 0, sizeof(context));
+
+ i = 0;
+ while ((c = getopt_long(argc, argv, "x:s:", aubinator_opts, &i)) != -1) {
+ switch (c) {
+ case 'x':
+ context.xml_path = strdup(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc)
+ context.input_file = argv[optind];
+
+ if (help || !context.input_file) {
+ print_help(argv[0], stderr);
+ exit(0);
+ }
+
+ context.file = aub_file_open(context.input_file);
+
+ gtk_init(NULL, NULL);
+
+ context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Aubinator Viewer");
+ g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
+ gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720);
+
+ GtkWidget* gl_area = gtk_gl_area_new();
+ g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL);
+ g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL);
+ g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL);
+ gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
+
+ gtk_widget_show_all(context.gtk_window);
+
+ show_aubfile_window();
+
+ gtk_main();
+
+ free(context.xml_path);
+
+ return EXIT_SUCCESS;
+}