Path: blob/21.2-virgl/src/intel/tools/aubinator_viewer.cpp
4547 views
/*1* Copyright © 2016 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#include <stdio.h>24#include <stdlib.h>25#include <stdint.h>26#include <getopt.h>27#include <unistd.h>28#include <fcntl.h>29#include <string.h>30#include <errno.h>31#include <sys/stat.h>32#include <sys/mman.h>33#include <sys/types.h>34#include <ctype.h>3536#include "util/macros.h"3738#include "aub_read.h"39#include "aub_mem.h"4041#include "common/intel_disasm.h"4243#define xtzalloc(name) ((decltype(&name)) calloc(1, sizeof(name)))44#define xtalloc(name) ((decltype(&name)) malloc(sizeof(name)))4546struct aub_file {47uint8_t *map, *end, *cursor;4849uint16_t pci_id;50char app_name[33];5152/* List of batch buffers to process */53struct {54const uint8_t *start;55const uint8_t *end;56} *execs;57int n_execs;58int n_allocated_execs;5960uint32_t idx_reg_write;6162/* Device state */63struct intel_device_info devinfo;64struct intel_spec *spec;65};6667static void68store_exec_begin(struct aub_file *file)69{70if (unlikely(file->n_execs >= file->n_allocated_execs)) {71file->n_allocated_execs = MAX2(2U * file->n_allocated_execs,724096 / sizeof(file->execs[0]));73file->execs = (decltype(file->execs))74realloc(static_cast<void *>(file->execs),75file->n_allocated_execs * sizeof(file->execs[0]));76}7778file->execs[file->n_execs++].start = file->cursor;79}8081static void82store_exec_end(struct aub_file *file)83{84if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)85file->execs[file->n_execs - 1].end = file->cursor;86}8788static void89handle_mem_write(void *user_data, uint64_t phys_addr,90const void *data, uint32_t data_len)91{92struct aub_file *file = (struct aub_file *) user_data;93file->idx_reg_write = 0;94store_exec_end(file);95}9697static void98handle_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,99const void *ring_data, uint32_t ring_data_len)100{101struct aub_file *file = (struct aub_file *) user_data;102file->idx_reg_write = 0;103store_exec_begin(file);104}105106static void107handle_reg_write(void *user_data, uint32_t reg_offset, uint32_t reg_value)108{109struct aub_file *file = (struct aub_file *) user_data;110111/* Only store the first register write of a series (execlist writes take112* involve 2 dwords).113*/114if (file->idx_reg_write++ == 0)115store_exec_begin(file);116}117118static void119handle_info(void *user_data, int pci_id, const char *app_name)120{121struct aub_file *file = (struct aub_file *) user_data;122store_exec_end(file);123124file->pci_id = pci_id;125snprintf(file->app_name, sizeof(app_name), "%s", app_name);126127if (!intel_get_device_info_from_pci_id(file->pci_id, &file->devinfo)) {128fprintf(stderr, "can't find device information: pci_id=0x%x\n", file->pci_id);129exit(EXIT_FAILURE);130}131file->spec = intel_spec_load(&file->devinfo);132}133134static void135handle_error(void *user_data, const void *aub_data, const char *msg)136{137fprintf(stderr, "ERROR: %s", msg);138}139140static struct aub_file *141aub_file_open(const char *filename)142{143struct aub_file *file;144struct stat sb;145int fd;146147file = xtzalloc(*file);148fd = open(filename, O_RDWR);149if (fd == -1) {150fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno));151exit(EXIT_FAILURE);152}153154if (fstat(fd, &sb) == -1) {155fprintf(stderr, "stat failed: %s\n", strerror(errno));156exit(EXIT_FAILURE);157}158159file->map = (uint8_t *) mmap(NULL, sb.st_size,160PROT_READ, MAP_SHARED, fd, 0);161if (file->map == MAP_FAILED) {162fprintf(stderr, "mmap failed: %s\n", strerror(errno));163exit(EXIT_FAILURE);164}165166close(fd);167168file->cursor = file->map;169file->end = file->map + sb.st_size;170171struct aub_read aub_read = {};172aub_read.user_data = file;173aub_read.info = handle_info;174aub_read.error = handle_error;175aub_read.reg_write = handle_reg_write;176aub_read.ring_write = handle_ring_write;177aub_read.local_write = handle_mem_write;178aub_read.phys_write = handle_mem_write;179aub_read.ggtt_write = handle_mem_write;180aub_read.ggtt_entry_write = handle_mem_write;181182int consumed;183while (file->cursor < file->end &&184(consumed = aub_read_command(&aub_read, file->cursor,185file->end - file->cursor)) > 0) {186file->cursor += consumed;187}188189/* Ensure we have an end on the last register write. */190if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL)191file->execs[file->n_execs - 1].end = file->end;192193return file;194}195196/**/197198static void199update_mem_for_exec(struct aub_mem *mem, struct aub_file *file, int exec_idx)200{201struct aub_read read = {};202read.user_data = mem;203read.local_write = aub_mem_local_write;204read.phys_write = aub_mem_phys_write;205read.ggtt_write = aub_mem_ggtt_write;206read.ggtt_entry_write = aub_mem_ggtt_entry_write;207208/* Replay the aub file from the beginning up to just before the209* commands we want to read. where the context setup happens.210*/211const uint8_t *iter = file->map;212while (iter < file->execs[exec_idx].start) {213iter += aub_read_command(&read, iter, file->execs[exec_idx].start - iter);214}215}216217/* UI */218219#include <epoxy/gl.h>220221#include "imgui/imgui.h"222#include "imgui/imgui_memory_editor.h"223#include "imgui_impl_gtk3.h"224#include "imgui_impl_opengl3.h"225226#include "aubinator_viewer.h"227#include "aubinator_viewer_urb.h"228229struct window {230struct list_head link; /* link in the global list of windows */231struct list_head parent_link; /* link in parent window list of children */232233struct list_head children_windows; /* list of children windows */234235char name[128];236bool opened;237238ImVec2 position;239ImVec2 size;240241void (*display)(struct window*);242void (*destroy)(struct window*);243};244245struct edit_window {246struct window base;247248struct aub_mem *mem;249uint64_t address;250uint32_t len;251252struct intel_batch_decode_bo aub_bo;253uint64_t aub_offset;254255struct intel_batch_decode_bo gtt_bo;256uint64_t gtt_offset;257258struct MemoryEditor editor;259};260261struct pml4_window {262struct window base;263264struct aub_mem *mem;265};266267struct shader_window {268struct window base;269270uint64_t address;271char *shader;272size_t shader_size;273};274275struct urb_window {276struct window base;277278uint32_t end_urb_offset;279struct aub_decode_urb_stage_state urb_stages[AUB_DECODE_N_STAGE];280281AubinatorViewerUrb urb_view;282};283284struct batch_window {285struct window base;286287struct aub_mem mem;288struct aub_read read;289290bool uses_ppgtt;291292bool collapsed;293int exec_idx;294295struct aub_viewer_decode_cfg decode_cfg;296struct aub_viewer_decode_ctx decode_ctx;297298struct pml4_window pml4_window;299300char edit_address[20];301};302303static struct Context {304struct aub_file *file;305char *input_file;306char *xml_path;307308GtkWidget *gtk_window;309310/* UI state*/311bool show_commands_window;312bool show_registers_window;313314struct aub_viewer_cfg cfg;315316struct list_head windows;317318struct window file_window;319struct window commands_window;320struct window registers_window;321} context;322323thread_local ImGuiContext* __MesaImGui;324325static int326map_key(int k)327{328return ImGuiKey_COUNT + k;329}330331static bool332has_ctrl_key(int key)333{334return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));335}336337static bool338window_has_ctrl_key(int key)339{340return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);341}342343static void344destroy_window_noop(struct window *win)345{346}347348/* Shader windows */349350static void351display_shader_window(struct window *win)352{353struct shader_window *window = (struct shader_window *) win;354355if (window->shader) {356ImGui::InputTextMultiline("Assembly",357window->shader, window->shader_size,358ImGui::GetContentRegionAvail(),359ImGuiInputTextFlags_ReadOnly);360} else {361ImGui::Text("Shader not available");362}363}364365static void366destroy_shader_window(struct window *win)367{368struct shader_window *window = (struct shader_window *) win;369370free(window->shader);371free(window);372}373374static struct shader_window *375new_shader_window(struct aub_mem *mem, uint64_t address, const char *desc)376{377struct shader_window *window = xtzalloc(*window);378379snprintf(window->base.name, sizeof(window->base.name),380"%s (0x%" PRIx64 ")##%p", desc, address, window);381382list_inithead(&window->base.parent_link);383window->base.position = ImVec2(-1, -1);384window->base.size = ImVec2(700, 300);385window->base.opened = true;386window->base.display = display_shader_window;387window->base.destroy = destroy_shader_window;388389struct intel_batch_decode_bo shader_bo =390aub_mem_get_ppgtt_bo(mem, address);391if (shader_bo.map) {392FILE *f = open_memstream(&window->shader, &window->shader_size);393if (f) {394intel_disassemble(&context.file->devinfo,395(const uint8_t *) shader_bo.map +396(address - shader_bo.addr), 0, f);397fclose(f);398}399}400401list_addtail(&window->base.link, &context.windows);402403return window;404}405406/* URB windows */407408static void409display_urb_window(struct window *win)410{411struct urb_window *window = (struct urb_window *) win;412static const char *stages[] = {413[AUB_DECODE_STAGE_VS] = "VS",414[AUB_DECODE_STAGE_HS] = "HS",415[AUB_DECODE_STAGE_DS] = "DS",416[AUB_DECODE_STAGE_GS] = "GS",417[AUB_DECODE_STAGE_PS] = "PS",418[AUB_DECODE_STAGE_CS] = "CS",419};420421ImGui::Text("URB allocation:");422window->urb_view.DrawAllocation("##urb",423ARRAY_SIZE(window->urb_stages),424window->end_urb_offset,425stages,426&window->urb_stages[0]);427}428429static void430destroy_urb_window(struct window *win)431{432struct urb_window *window = (struct urb_window *) win;433434free(window);435}436437static struct urb_window *438new_urb_window(struct aub_viewer_decode_ctx *decode_ctx, uint64_t address)439{440struct urb_window *window = xtzalloc(*window);441442snprintf(window->base.name, sizeof(window->base.name),443"URB view (0x%" PRIx64 ")##%p", address, window);444445list_inithead(&window->base.parent_link);446window->base.position = ImVec2(-1, -1);447window->base.size = ImVec2(700, 300);448window->base.opened = true;449window->base.display = display_urb_window;450window->base.destroy = destroy_urb_window;451452window->end_urb_offset = decode_ctx->end_urb_offset;453memcpy(window->urb_stages, decode_ctx->urb_stages, sizeof(window->urb_stages));454window->urb_view = AubinatorViewerUrb();455456list_addtail(&window->base.link, &context.windows);457458return window;459}460461/* Memory editor windows */462463static uint8_t464read_edit_window(const uint8_t *data, size_t off)465{466struct edit_window *window = (struct edit_window *) data;467468return *((const uint8_t *) window->gtt_bo.map + window->gtt_offset + off);469}470471static void472write_edit_window(uint8_t *data, size_t off, uint8_t d)473{474struct edit_window *window = (struct edit_window *) data;475uint8_t *gtt = (uint8_t *) window->gtt_bo.map + window->gtt_offset + off;476uint8_t *aub = (uint8_t *) window->aub_bo.map + window->aub_offset + off;477478*gtt = *aub = d;479}480481static void482display_edit_window(struct window *win)483{484struct edit_window *window = (struct edit_window *) win;485486if (window->aub_bo.map && window->gtt_bo.map) {487ImGui::BeginChild(ImGui::GetID("##block"));488window->editor.DrawContents((uint8_t *) window,489MIN3(window->len,490window->gtt_bo.size - window->gtt_offset,491window->aub_bo.size - window->aub_offset),492window->address);493ImGui::EndChild();494} else {495ImGui::Text("Memory view at 0x%" PRIx64 " not available", window->address);496}497}498499static void500destroy_edit_window(struct window *win)501{502struct edit_window *window = (struct edit_window *) win;503504if (window->aub_bo.map)505mprotect((void *) window->aub_bo.map, 4096, PROT_READ);506free(window);507}508509static struct edit_window *510new_edit_window(struct aub_mem *mem, uint64_t address, uint32_t len)511{512struct edit_window *window = xtzalloc(*window);513514snprintf(window->base.name, sizeof(window->base.name),515"Editing aub at 0x%" PRIx64 "##%p", address, window);516517list_inithead(&window->base.parent_link);518window->base.position = ImVec2(-1, -1);519window->base.size = ImVec2(500, 600);520window->base.opened = true;521window->base.display = display_edit_window;522window->base.destroy = destroy_edit_window;523524window->mem = mem;525window->address = address;526window->aub_bo = aub_mem_get_ppgtt_addr_aub_data(mem, address);527window->gtt_bo = aub_mem_get_ppgtt_addr_data(mem, address);528window->len = len;529window->editor = MemoryEditor();530window->editor.OptShowDataPreview = true;531window->editor.OptShowAscii = false;532window->editor.ReadFn = read_edit_window;533window->editor.WriteFn = write_edit_window;534535if (window->aub_bo.map) {536uint64_t unaligned_map = (uint64_t) window->aub_bo.map;537window->aub_bo.map = (const void *)(unaligned_map & ~0xffful);538window->aub_offset = unaligned_map - (uint64_t) window->aub_bo.map;539540if (mprotect((void *) window->aub_bo.map, window->aub_bo.size, PROT_READ | PROT_WRITE) != 0) {541window->aub_bo.map = NULL;542}543}544545window->gtt_offset = address - window->gtt_bo.addr;546547list_addtail(&window->base.link, &context.windows);548549return window;550}551552/* 4 level page table walk windows */553554static void555display_pml4_level(struct aub_mem *mem, uint64_t table_addr, uint64_t table_virt_addr, int level)556{557if (level == 0)558return;559560struct intel_batch_decode_bo table_bo =561aub_mem_get_phys_addr_data(mem, table_addr);562const uint64_t *table = (const uint64_t *) ((const uint8_t *) table_bo.map +563table_addr - table_bo.addr);564if (!table) {565ImGui::TextColored(context.cfg.missing_color, "Page not available");566return;567}568569uint64_t addr_increment = 1ULL << (12 + 9 * (level - 1));570571if (level == 1) {572for (int e = 0; e < 512; e++) {573bool available = (table[e] & 1) != 0;574uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;575if (!available)576continue;577ImGui::Text("Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,578e, table[e], entry_virt_addr);579}580} else {581for (int e = 0; e < 512; e++) {582bool available = (table[e] & 1) != 0;583uint64_t entry_virt_addr = table_virt_addr + e * addr_increment;584if (available &&585ImGui::TreeNodeEx(&table[e],586available ? ImGuiTreeNodeFlags_Framed : 0,587"Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,588e, table[e], entry_virt_addr)) {589display_pml4_level(mem, table[e] & ~0xffful, entry_virt_addr, level -1);590ImGui::TreePop();591}592}593}594}595596static void597display_pml4_window(struct window *win)598{599struct pml4_window *window = (struct pml4_window *) win;600601ImGui::Text("pml4: %" PRIx64, window->mem->pml4);602ImGui::BeginChild(ImGui::GetID("##block"));603display_pml4_level(window->mem, window->mem->pml4, 0, 4);604ImGui::EndChild();605}606607static void608show_pml4_window(struct pml4_window *window, struct aub_mem *mem)609{610if (window->base.opened) {611window->base.opened = false;612return;613}614615snprintf(window->base.name, sizeof(window->base.name),616"4-Level page tables##%p", window);617618list_inithead(&window->base.parent_link);619window->base.position = ImVec2(-1, -1);620window->base.size = ImVec2(500, 600);621window->base.opened = true;622window->base.display = display_pml4_window;623window->base.destroy = destroy_window_noop;624625window->mem = mem;626627list_addtail(&window->base.link, &context.windows);628}629630/* Batch decoding windows */631632static void633display_decode_options(struct aub_viewer_decode_cfg *cfg)634{635char name[40];636snprintf(name, sizeof(name), "command filter##%p", &cfg->command_filter);637cfg->command_filter.Draw(name); ImGui::SameLine();638snprintf(name, sizeof(name), "field filter##%p", &cfg->field_filter);639cfg->field_filter.Draw(name); ImGui::SameLine();640if (ImGui::Button("Dwords")) cfg->show_dwords ^= 1;641}642643static void644batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)645{646struct batch_window *window = (struct batch_window *) user_data;647struct shader_window *shader_window =648new_shader_window(&window->mem, address, shader_desc);649650list_add(&shader_window->base.parent_link, &window->base.children_windows);651}652653static void654batch_display_urb(void *user_data, const struct aub_decode_urb_stage_state *stages)655{656struct batch_window *window = (struct batch_window *) user_data;657struct urb_window *urb_window = new_urb_window(&window->decode_ctx, 0);658659list_add(&urb_window->base.parent_link, &window->base.children_windows);660}661662static void663batch_edit_address(void *user_data, uint64_t address, uint32_t len)664{665struct batch_window *window = (struct batch_window *) user_data;666struct edit_window *edit_window =667new_edit_window(&window->mem, address, len);668669list_add(&edit_window->base.parent_link, &window->base.children_windows);670}671672static struct intel_batch_decode_bo673batch_get_bo(void *user_data, bool ppgtt, uint64_t address)674{675struct batch_window *window = (struct batch_window *) user_data;676677if (window->uses_ppgtt && ppgtt)678return aub_mem_get_ppgtt_bo(&window->mem, address);679else680return aub_mem_get_ggtt_bo(&window->mem, address);681}682683static void684update_batch_window(struct batch_window *window, bool reset, int exec_idx)685{686if (reset)687aub_mem_fini(&window->mem);688aub_mem_init(&window->mem);689690window->exec_idx = MAX2(MIN2(context.file->n_execs - 1, exec_idx), 0);691update_mem_for_exec(&window->mem, context.file, window->exec_idx);692}693694static void695display_batch_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,696const void *data, uint32_t data_len)697{698struct batch_window *window = (struct batch_window *) user_data;699700window->uses_ppgtt = false;701702aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0, false);703}704705static void706display_batch_execlist_write(void *user_data,707enum drm_i915_gem_engine_class engine,708uint64_t context_descriptor)709{710struct batch_window *window = (struct batch_window *) user_data;711712const uint32_t pphwsp_size = 4096;713uint32_t pphwsp_addr = context_descriptor & 0xfffff000;714struct intel_batch_decode_bo pphwsp_bo =715aub_mem_get_ggtt_bo(&window->mem, pphwsp_addr);716uint32_t *context_img = (uint32_t *)((uint8_t *)pphwsp_bo.map +717(pphwsp_addr - pphwsp_bo.addr) +718pphwsp_size);719720uint32_t ring_buffer_head = context_img[5];721uint32_t ring_buffer_tail = context_img[7];722uint32_t ring_buffer_start = context_img[9];723uint32_t ring_buffer_length = (context_img[11] & 0x1ff000) + 4096;724725window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51];726727struct intel_batch_decode_bo ring_bo =728aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start);729assert(ring_bo.size > 0);730void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head;731732window->uses_ppgtt = true;733734window->decode_ctx.engine = engine;735aub_viewer_render_batch(&window->decode_ctx, commands,736MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length),737ring_buffer_start + ring_buffer_head, true);738}739740static void741display_batch_window(struct window *win)742{743struct batch_window *window = (struct batch_window *) win;744745ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));746if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();747display_decode_options(&window->decode_cfg);748ImGui::PopItemWidth();749750if (ImGui::InputInt("Execbuf", &window->exec_idx))751update_batch_window(window, true, window->exec_idx);752753if (window_has_ctrl_key('p'))754update_batch_window(window, true, window->exec_idx - 1);755if (window_has_ctrl_key('n'))756update_batch_window(window, true, window->exec_idx + 1);757758ImGui::Text("execbuf %i", window->exec_idx);759if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window->pml4_window, &window->mem); }760761ImGui::BeginChild(ImGui::GetID("##block"));762763struct aub_read read = {};764read.user_data = window;765read.ring_write = display_batch_ring_write;766read.execlist_write = display_batch_execlist_write;767768const uint8_t *iter = context.file->execs[window->exec_idx].start;769while (iter < context.file->execs[window->exec_idx].end) {770iter += aub_read_command(&read, iter,771context.file->execs[window->exec_idx].end - iter);772}773774ImGui::EndChild();775}776777static void778destroy_batch_window(struct window *win)779{780struct batch_window *window = (struct batch_window *) win;781782aub_mem_fini(&window->mem);783784/* This works because children windows are inserted at the back of the785* list, ensuring the deletion loop goes through the children after calling786* this function.787*/788list_for_each_entry(struct window, child_window,789&window->base.children_windows, parent_link)790child_window->opened = false;791window->pml4_window.base.opened = false;792793free(window);794}795796static void797new_batch_window(int exec_idx)798{799struct batch_window *window = xtzalloc(*window);800801snprintf(window->base.name, sizeof(window->base.name),802"Batch view##%p", window);803804list_inithead(&window->base.parent_link);805list_inithead(&window->base.children_windows);806window->base.position = ImVec2(-1, -1);807window->base.size = ImVec2(600, 700);808window->base.opened = true;809window->base.display = display_batch_window;810window->base.destroy = destroy_batch_window;811812window->collapsed = true;813window->decode_cfg = aub_viewer_decode_cfg();814815aub_viewer_decode_ctx_init(&window->decode_ctx,816&context.cfg,817&window->decode_cfg,818&context.file->devinfo,819context.file->spec,820batch_get_bo,821NULL,822window);823window->decode_ctx.display_shader = batch_display_shader;824window->decode_ctx.display_urb = batch_display_urb;825window->decode_ctx.edit_address = batch_edit_address;826827update_batch_window(window, false, exec_idx);828829list_addtail(&window->base.link, &context.windows);830}831832/**/833834static void835display_registers_window(struct window *win)836{837static struct ImGuiTextFilter filter;838if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();839filter.Draw();840841ImGui::BeginChild(ImGui::GetID("##block"));842hash_table_foreach(context.file->spec->registers_by_name, entry) {843struct intel_group *reg = (struct intel_group *) entry->data;844if (filter.PassFilter(reg->name) &&845ImGui::CollapsingHeader(reg->name)) {846const struct intel_field *field = reg->fields;847while (field) {848ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);849field = field->next;850}851}852}853ImGui::EndChild();854}855856static void857show_register_window(void)858{859struct window *window = &context.registers_window;860861if (window->opened) {862window->opened = false;863return;864}865866snprintf(window->name, sizeof(window->name), "Registers");867868list_inithead(&window->parent_link);869window->position = ImVec2(-1, -1);870window->size = ImVec2(200, 400);871window->opened = true;872window->display = display_registers_window;873window->destroy = destroy_window_noop;874875list_addtail(&window->link, &context.windows);876}877878static void879display_commands_window(struct window *win)880{881static struct ImGuiTextFilter cmd_filter;882if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();883cmd_filter.Draw("name filter");884static struct ImGuiTextFilter field_filter;885field_filter.Draw("field filter");886887static char opcode_str[9] = { 0, };888ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str),889ImGuiInputTextFlags_CharsHexadecimal);890size_t opcode_len = strlen(opcode_str);891uint64_t opcode = strtol(opcode_str, NULL, 16);892893static bool show_dwords = true;894if (ImGui::Button("Dwords")) show_dwords ^= 1;895896ImGui::BeginChild(ImGui::GetID("##block"));897hash_table_foreach(context.file->spec->commands, entry) {898struct intel_group *cmd = (struct intel_group *) entry->data;899if ((cmd_filter.PassFilter(cmd->name) &&900(opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&901ImGui::CollapsingHeader(cmd->name)) {902const struct intel_field *field = cmd->fields;903int32_t last_dword = -1;904while (field) {905if (show_dwords && field->start / 32 != last_dword) {906for (last_dword = MAX2(0, last_dword + 1);907last_dword < field->start / 32; last_dword++) {908ImGui::TextColored(context.cfg.dwords_color,909"Dword %d", last_dword);910}911ImGui::TextColored(context.cfg.dwords_color, "Dword %d", last_dword);912}913if (field_filter.PassFilter(field->name))914ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);915field = field->next;916}917}918}919hash_table_foreach(context.file->spec->structs, entry) {920struct intel_group *cmd = (struct intel_group *) entry->data;921if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&922ImGui::CollapsingHeader(cmd->name)) {923const struct intel_field *field = cmd->fields;924int32_t last_dword = -1;925while (field) {926if (show_dwords && field->start / 32 != last_dword) {927last_dword = field->start / 32;928ImGui::TextColored(context.cfg.dwords_color,929"Dword %d", last_dword);930}931if (field_filter.PassFilter(field->name))932ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end);933field = field->next;934}935}936}937ImGui::EndChild();938}939940static void941show_commands_window(void)942{943struct window *window = &context.commands_window;944945if (window->opened) {946window->opened = false;947return;948}949950snprintf(window->name, sizeof(window->name), "Commands & structs");951952list_inithead(&window->parent_link);953window->position = ImVec2(-1, -1);954window->size = ImVec2(300, 400);955window->opened = true;956window->display = display_commands_window;957window->destroy = destroy_window_noop;958959list_addtail(&window->link, &context.windows);960}961962/* Main window */963964static const char *965human_size(size_t size)966{967unsigned divisions = 0;968double v = size;969double divider = 1024;970while (v >= divider) {971v /= divider;972divisions++;973}974975static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };976static char result[20];977snprintf(result, sizeof(result), "%.2f %s",978v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);979return result;980}981982static void983display_aubfile_window(struct window *win)984{985ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |986ImGuiColorEditFlags_NoLabel |987ImGuiColorEditFlags_NoInputs);988struct aub_viewer_cfg *cfg = &context.cfg;989990ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine();991ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine();992ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine();993ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine();994ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine();995ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();996997if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();998if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();999if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }10001001if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }10021003ImGui::Text("File name: %s", context.input_file);1004ImGui::Text("File size: %s", human_size(context.file->end - context.file->map));1005ImGui::Text("Execbufs %u", context.file->n_execs);1006ImGui::Text("PCI ID: 0x%x", context.file->pci_id);1007ImGui::Text("Application name: %s", context.file->app_name);1008ImGui::Text("%s", intel_get_device_name(context.file->pci_id));10091010ImGui::SetNextWindowContentWidth(500);1011if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {1012ImGui::Text("Some global keybindings:");1013ImGui::Separator();10141015static const char *texts[] = {1016"Ctrl-h", "show this screen",1017"Ctrl-c", "show commands list",1018"Ctrl-r", "show registers list",1019"Ctrl-b", "new batch window",1020"Ctrl-p/n", "switch to previous/next batch buffer",1021"Ctrl-Tab", "switch focus between window",1022"Ctrl-left/right", "align window to the side of the screen",1023};1024float align = 0.0f;1025for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)1026align = MAX2(align, ImGui::CalcTextSize(texts[i]).x);1027align += ImGui::GetStyle().WindowPadding.x + 10;10281029for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {1030ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);1031}10321033if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))1034ImGui::CloseCurrentPopup();1035ImGui::EndPopup();1036}1037}10381039static void1040show_aubfile_window(void)1041{1042struct window *window = &context.file_window;10431044if (window->opened)1045return;10461047snprintf(window->name, sizeof(window->name),1048"Aubinator Viewer: Intel AUB file decoder/editor");10491050list_inithead(&window->parent_link);1051window->size = ImVec2(-1, 250);1052window->position = ImVec2(0, 0);1053window->opened = true;1054window->display = display_aubfile_window;1055window->destroy = NULL;10561057list_addtail(&window->link, &context.windows);1058}10591060/* Main redrawing */10611062static void1063display_windows(void)1064{1065/* Start by disposing closed windows, we don't want to destroy windows that1066* have already been scheduled to be painted. So destroy always happens on1067* the next draw cycle, prior to any drawing.1068*/1069list_for_each_entry_safe(struct window, window, &context.windows, link) {1070if (window->opened)1071continue;10721073/* Can't close this one. */1074if (window == &context.file_window) {1075window->opened = true;1076continue;1077}10781079list_del(&window->link);1080list_del(&window->parent_link);1081if (window->destroy)1082window->destroy(window);1083}10841085list_for_each_entry_safe(struct window, window, &context.windows, link) {1086ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver);1087ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver);1088if (ImGui::Begin(window->name, &window->opened)) {1089window->display(window);1090window->position = ImGui::GetWindowPos();1091window->size = ImGui::GetWindowSize();1092}1093if (window_has_ctrl_key('w'))1094window->opened = false;1095ImGui::End();1096}1097}10981099static void1100repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context)1101{1102ImGui_ImplOpenGL3_NewFrame();1103ImGui_ImplGtk3_NewFrame();1104ImGui::NewFrame();11051106display_windows();11071108ImGui::EndFrame();1109ImGui::Render();11101111glClearColor(context.cfg.clear_color.Value.x,1112context.cfg.clear_color.Value.y,1113context.cfg.clear_color.Value.z, 1.0);1114glClear(GL_COLOR_BUFFER_BIT);1115ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());1116}11171118static void1119realize_area(GtkGLArea *area)1120{1121ImGui::CreateContext();1122ImGui_ImplGtk3_Init(GTK_WIDGET(area), true);1123ImGui_ImplOpenGL3_Init("#version 130");11241125list_inithead(&context.windows);11261127ImGui::StyleColorsDark();1128context.cfg = aub_viewer_cfg();11291130ImGuiIO& io = ImGui::GetIO();1131io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;1132}11331134static void1135unrealize_area(GtkGLArea *area)1136{1137gtk_gl_area_make_current(area);11381139ImGui_ImplOpenGL3_Shutdown();1140ImGui_ImplGtk3_Shutdown();1141ImGui::DestroyContext();1142}11431144static void1145size_allocate_area(GtkGLArea *area,1146GdkRectangle *allocation,1147gpointer user_data)1148{1149if (!gtk_widget_get_realized(GTK_WIDGET(area)))1150return;11511152/* We want to catch only initial size allocate. */1153g_signal_handlers_disconnect_by_func(area,1154(gpointer) size_allocate_area,1155user_data);1156show_aubfile_window();1157}11581159static void1160print_help(const char *progname, FILE *file)1161{1162fprintf(file,1163"Usage: %s [OPTION]... FILE\n"1164"Decode aub file contents from FILE.\n\n"1165" --help display this help and exit\n"1166" -x, --xml=DIR load hardware xml description from directory DIR\n",1167progname);1168}11691170int main(int argc, char *argv[])1171{1172int c, i;1173bool help = false;1174const struct option aubinator_opts[] = {1175{ "help", no_argument, (int *) &help, true },1176{ "xml", required_argument, NULL, 'x' },1177{ NULL, 0, NULL, 0 }1178};11791180memset(&context, 0, sizeof(context));11811182i = 0;1183while ((c = getopt_long(argc, argv, "x:s:", aubinator_opts, &i)) != -1) {1184switch (c) {1185case 'x':1186context.xml_path = strdup(optarg);1187break;1188default:1189break;1190}1191}11921193if (optind < argc)1194context.input_file = argv[optind];11951196if (help || !context.input_file) {1197print_help(argv[0], stderr);1198exit(0);1199}12001201context.file = aub_file_open(context.input_file);12021203gtk_init(NULL, NULL);12041205context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);1206gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Aubinator Viewer");1207g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);1208gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720);12091210GtkWidget* gl_area = gtk_gl_area_new();1211g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL);1212g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL);1213g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL);1214g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);1215gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);12161217gtk_widget_show_all(context.gtk_window);12181219gtk_main();12201221free(context.xml_path);12221223return EXIT_SUCCESS;1224}122512261227