2018-07-28 19:13:28 +01:00
|
|
|
/*
|
|
|
|
|
* 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"
|
|
|
|
|
|
2024-01-24 23:12:52 -08:00
|
|
|
#include "compiler/brw_disasm.h"
|
|
|
|
|
#include "compiler/brw_isa_info.h"
|
2024-02-09 09:29:21 -08:00
|
|
|
#include "compiler/elk/elk_disasm.h"
|
|
|
|
|
#include "compiler/elk/elk_isa_info.h"
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
#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 */
|
2021-04-05 13:19:39 -07:00
|
|
|
struct intel_device_info devinfo;
|
2024-02-09 09:29:21 -08:00
|
|
|
struct brw_isa_info brw;
|
|
|
|
|
struct elk_isa_info elk;
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_spec *spec;
|
2018-07-28 19:13:28 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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
|
2022-10-05 12:52:05 -07:00
|
|
|
handle_ring_write(void *user_data, enum intel_engine_class engine,
|
2018-07-28 19:13:28 +01:00
|
|
|
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);
|
|
|
|
|
|
2021-04-05 15:59:35 -07:00
|
|
|
if (!intel_get_device_info_from_pci_id(file->pci_id, &file->devinfo)) {
|
2018-07-28 19:13:28 +01:00
|
|
|
fprintf(stderr, "can't find device information: pci_id=0x%x\n", file->pci_id);
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
2024-02-09 09:29:21 -08:00
|
|
|
if (file->devinfo.ver >= 9)
|
|
|
|
|
brw_init_isa_info(&file->brw, &file->devinfo);
|
|
|
|
|
else
|
|
|
|
|
elk_init_isa_info(&file->elk, &file->devinfo);
|
2021-03-03 13:49:18 -08:00
|
|
|
file->spec = intel_spec_load(&file->devinfo);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
handle_error(void *user_data, const void *aub_data, const char *msg)
|
|
|
|
|
{
|
2021-02-10 15:06:59 +01:00
|
|
|
fprintf(stderr, "ERROR: %s", msg);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
2019-01-19 20:02:13 +00:00
|
|
|
#include "imgui/imgui.h"
|
|
|
|
|
#include "imgui/imgui_memory_editor.h"
|
2018-07-28 19:13:28 +01:00
|
|
|
#include "imgui_impl_gtk3.h"
|
|
|
|
|
#include "imgui_impl_opengl3.h"
|
|
|
|
|
|
|
|
|
|
#include "aubinator_viewer.h"
|
2018-08-03 10:41:01 +01:00
|
|
|
#include "aubinator_viewer_urb.h"
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo aub_bo;
|
2018-07-28 19:13:28 +01:00
|
|
|
uint64_t aub_offset;
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo gtt_bo;
|
2018-07-28 19:13:28 +01:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-03 10:41:01 +01:00
|
|
|
struct urb_window {
|
|
|
|
|
struct window base;
|
|
|
|
|
|
|
|
|
|
uint32_t end_urb_offset;
|
|
|
|
|
struct aub_decode_urb_stage_state urb_stages[AUB_DECODE_N_STAGE];
|
|
|
|
|
|
|
|
|
|
AubinatorViewerUrb urb_view;
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-28 19:13:28 +01:00
|
|
|
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;
|
|
|
|
|
|
2019-01-22 13:04:20 +00:00
|
|
|
thread_local ImGuiContext* __MesaImGui;
|
|
|
|
|
|
2018-07-28 19:13:28 +01:00
|
|
|
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),
|
2020-03-26 14:07:46 +02:00
|
|
|
"%s (0x%" PRIx64 ")##%p", desc, address, window);
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo shader_bo =
|
2018-09-14 21:19:21 +01:00
|
|
|
aub_mem_get_ppgtt_bo(mem, address);
|
2018-07-28 19:13:28 +01:00
|
|
|
if (shader_bo.map) {
|
|
|
|
|
FILE *f = open_memstream(&window->shader, &window->shader_size);
|
|
|
|
|
if (f) {
|
2024-02-09 09:29:21 -08:00
|
|
|
if (context.file->devinfo.ver >= 9) {
|
|
|
|
|
brw_disassemble_with_errors(&context.file->brw,
|
|
|
|
|
(const uint8_t *) shader_bo.map +
|
|
|
|
|
(address - shader_bo.addr), 0, f);
|
|
|
|
|
} else {
|
|
|
|
|
elk_disassemble_with_errors(&context.file->elk,
|
|
|
|
|
(const uint8_t *) shader_bo.map +
|
|
|
|
|
(address - shader_bo.addr), 0, f);
|
|
|
|
|
}
|
2018-07-28 19:13:28 +01:00
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_addtail(&window->base.link, &context.windows);
|
|
|
|
|
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 10:41:01 +01:00
|
|
|
/* URB windows */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
display_urb_window(struct window *win)
|
|
|
|
|
{
|
|
|
|
|
struct urb_window *window = (struct urb_window *) win;
|
|
|
|
|
static const char *stages[] = {
|
|
|
|
|
[AUB_DECODE_STAGE_VS] = "VS",
|
|
|
|
|
[AUB_DECODE_STAGE_HS] = "HS",
|
|
|
|
|
[AUB_DECODE_STAGE_DS] = "DS",
|
|
|
|
|
[AUB_DECODE_STAGE_GS] = "GS",
|
|
|
|
|
[AUB_DECODE_STAGE_PS] = "PS",
|
|
|
|
|
[AUB_DECODE_STAGE_CS] = "CS",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImGui::Text("URB allocation:");
|
|
|
|
|
window->urb_view.DrawAllocation("##urb",
|
|
|
|
|
ARRAY_SIZE(window->urb_stages),
|
|
|
|
|
window->end_urb_offset,
|
|
|
|
|
stages,
|
|
|
|
|
&window->urb_stages[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
destroy_urb_window(struct window *win)
|
|
|
|
|
{
|
|
|
|
|
struct urb_window *window = (struct urb_window *) win;
|
|
|
|
|
|
|
|
|
|
free(window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct urb_window *
|
|
|
|
|
new_urb_window(struct aub_viewer_decode_ctx *decode_ctx, uint64_t address)
|
|
|
|
|
{
|
|
|
|
|
struct urb_window *window = xtzalloc(*window);
|
|
|
|
|
|
|
|
|
|
snprintf(window->base.name, sizeof(window->base.name),
|
2020-03-26 14:07:46 +02:00
|
|
|
"URB view (0x%" PRIx64 ")##%p", address, window);
|
2018-08-03 10:41:01 +01:00
|
|
|
|
|
|
|
|
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_urb_window;
|
|
|
|
|
window->base.destroy = destroy_urb_window;
|
|
|
|
|
|
|
|
|
|
window->end_urb_offset = decode_ctx->end_urb_offset;
|
|
|
|
|
memcpy(window->urb_stages, decode_ctx->urb_stages, sizeof(window->urb_stages));
|
|
|
|
|
window->urb_view = AubinatorViewerUrb();
|
|
|
|
|
|
|
|
|
|
list_addtail(&window->base.link, &context.windows);
|
|
|
|
|
|
|
|
|
|
return window;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 19:13:28 +01:00
|
|
|
/* 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 {
|
2020-03-26 14:07:46 +02:00
|
|
|
ImGui::Text("Memory view at 0x%" PRIx64 " not available", window->address);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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),
|
2020-03-26 14:07:46 +02:00
|
|
|
"Editing aub at 0x%" PRIx64 "##%p", address, window);
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo table_bo =
|
2018-07-28 19:13:28 +01:00
|
|
|
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;
|
2020-03-26 14:07:46 +02:00
|
|
|
ImGui::Text("Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
|
2018-07-28 19:13:28 +01:00
|
|
|
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,
|
2020-03-26 14:07:46 +02:00
|
|
|
"Entry%03i - phys_addr=0x%" PRIx64 " - virt_addr=0x%" PRIx64,
|
2018-07-28 19:13:28 +01:00
|
|
|
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;
|
|
|
|
|
|
2020-03-26 14:07:46 +02:00
|
|
|
ImGui::Text("pml4: %" PRIx64, window->mem->pml4);
|
2018-07-28 19:13:28 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 10:41:01 +01:00
|
|
|
static void
|
|
|
|
|
batch_display_urb(void *user_data, const struct aub_decode_urb_stage_state *stages)
|
|
|
|
|
{
|
|
|
|
|
struct batch_window *window = (struct batch_window *) user_data;
|
|
|
|
|
struct urb_window *urb_window = new_urb_window(&window->decode_ctx, 0);
|
|
|
|
|
|
|
|
|
|
list_add(&urb_window->base.parent_link, &window->base.children_windows);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 19:13:28 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
static struct intel_batch_decode_bo
|
2018-08-26 13:52:47 +01:00
|
|
|
batch_get_bo(void *user_data, bool ppgtt, uint64_t address)
|
2018-07-28 19:13:28 +01:00
|
|
|
{
|
|
|
|
|
struct batch_window *window = (struct batch_window *) user_data;
|
|
|
|
|
|
2018-08-26 13:52:47 +01:00
|
|
|
if (window->uses_ppgtt && ppgtt)
|
2018-07-28 19:13:28 +01:00
|
|
|
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
|
2022-10-05 12:52:05 -07:00
|
|
|
display_batch_ring_write(void *user_data, enum intel_engine_class engine,
|
2018-07-28 19:13:28 +01:00
|
|
|
const void *data, uint32_t data_len)
|
|
|
|
|
{
|
|
|
|
|
struct batch_window *window = (struct batch_window *) user_data;
|
|
|
|
|
|
|
|
|
|
window->uses_ppgtt = false;
|
|
|
|
|
|
2018-08-28 11:41:42 +01:00
|
|
|
aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0, false);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2018-11-07 16:50:32 +02:00
|
|
|
display_batch_execlist_write(void *user_data,
|
2022-10-05 12:52:05 -07:00
|
|
|
enum intel_engine_class engine,
|
2018-07-28 19:13:28 +01:00
|
|
|
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;
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo pphwsp_bo =
|
2018-07-28 19:13:28 +01:00
|
|
|
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];
|
2018-11-09 16:49:10 +00:00
|
|
|
uint32_t ring_buffer_length = (context_img[11] & 0x1ff000) + 4096;
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51];
|
|
|
|
|
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_batch_decode_bo ring_bo =
|
2018-07-28 19:13:28 +01:00
|
|
|
aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start);
|
|
|
|
|
assert(ring_bo.size > 0);
|
2018-11-09 16:49:11 +00:00
|
|
|
void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head;
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
window->uses_ppgtt = true;
|
|
|
|
|
|
2018-11-07 16:50:32 +02:00
|
|
|
window->decode_ctx.engine = engine;
|
2018-07-28 19:13:28 +01:00
|
|
|
aub_viewer_render_batch(&window->decode_ctx, commands,
|
2018-11-09 16:49:10 +00:00
|
|
|
MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length),
|
2018-08-28 11:41:42 +01:00
|
|
|
ring_buffer_start + ring_buffer_head, true);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2020-09-01 18:39:54 -05:00
|
|
|
&context.file->devinfo,
|
2018-07-28 19:13:28 +01:00
|
|
|
context.file->spec,
|
|
|
|
|
batch_get_bo,
|
|
|
|
|
NULL,
|
|
|
|
|
window);
|
|
|
|
|
window->decode_ctx.display_shader = batch_display_shader;
|
2018-08-03 10:41:01 +01:00
|
|
|
window->decode_ctx.display_urb = batch_display_urb;
|
2018-07-28 19:13:28 +01:00
|
|
|
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"));
|
|
|
|
|
hash_table_foreach(context.file->spec->registers_by_name, entry) {
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_group *reg = (struct intel_group *) entry->data;
|
2018-07-28 19:13:28 +01:00
|
|
|
if (filter.PassFilter(reg->name) &&
|
|
|
|
|
ImGui::CollapsingHeader(reg->name)) {
|
2021-03-03 13:49:18 -08:00
|
|
|
const struct intel_field *field = reg->fields;
|
2018-07-28 19:13:28 +01:00
|
|
|
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"));
|
|
|
|
|
hash_table_foreach(context.file->spec->commands, entry) {
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_group *cmd = (struct intel_group *) entry->data;
|
2018-07-28 19:13:28 +01:00
|
|
|
if ((cmd_filter.PassFilter(cmd->name) &&
|
|
|
|
|
(opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) &&
|
|
|
|
|
ImGui::CollapsingHeader(cmd->name)) {
|
2021-03-03 13:49:18 -08:00
|
|
|
const struct intel_field *field = cmd->fields;
|
2018-07-28 19:13:28 +01:00
|
|
|
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) {
|
2021-03-03 13:49:18 -08:00
|
|
|
struct intel_group *cmd = (struct intel_group *) entry->data;
|
2018-07-28 19:13:28 +01:00
|
|
|
if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 &&
|
|
|
|
|
ImGui::CollapsingHeader(cmd->name)) {
|
2021-03-03 13:49:18 -08:00
|
|
|
const struct intel_field *field = cmd->fields;
|
2018-07-28 19:13:28 +01:00
|
|
|
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();
|
2018-11-18 17:25:46 +00:00
|
|
|
ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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);
|
2021-07-13 17:56:01 -05:00
|
|
|
ImGui::Text("%s", context.file->devinfo.name);
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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) {
|
2019-02-25 10:50:59 +00:00
|
|
|
ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);
|
2018-07-28 19:13:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2018-09-11 10:32:32 +03:00
|
|
|
window->position = ImVec2(0, 0);
|
2018-07-28 19:13:28 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-17 14:42:49 +03:00
|
|
|
list_for_each_entry_safe(struct window, window, &context.windows, link) {
|
2018-07-28 19:13:28 +01:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-11 10:32:32 +03:00
|
|
|
static void
|
|
|
|
|
size_allocate_area(GtkGLArea *area,
|
|
|
|
|
GdkRectangle *allocation,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
if (!gtk_widget_get_realized(GTK_WIDGET(area)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* We want to catch only initial size allocate. */
|
|
|
|
|
g_signal_handlers_disconnect_by_func(area,
|
|
|
|
|
(gpointer) size_allocate_area,
|
|
|
|
|
user_data);
|
|
|
|
|
show_aubfile_window();
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-28 19:13:28 +01:00
|
|
|
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 }
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-07 20:47:48 +08:00
|
|
|
context = {};
|
2018-07-28 19:13:28 +01:00
|
|
|
|
|
|
|
|
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);
|
2018-09-11 10:32:32 +03:00
|
|
|
g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);
|
2018-07-28 19:13:28 +01:00
|
|
|
gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
|
|
|
|
|
|
|
|
|
|
gtk_widget_show_all(context.gtk_window);
|
|
|
|
|
|
|
|
|
|
gtk_main();
|
|
|
|
|
|
|
|
|
|
free(context.xml_path);
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|