2023-05-02 16:41:33 +03:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2023 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 <memory>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include "util/list.h"
|
|
|
|
|
#include "util/macros.h"
|
|
|
|
|
|
|
|
|
|
#include "common/intel_hang_dump.h"
|
2024-01-24 23:12:52 -08:00
|
|
|
#include "compiler/brw_disasm.h"
|
|
|
|
|
#include "compiler/brw_isa_info.h"
|
2023-05-02 16:41:33 +03:00
|
|
|
|
|
|
|
|
/* Data */
|
|
|
|
|
|
|
|
|
|
struct hang_bo {
|
|
|
|
|
void *map = NULL;
|
|
|
|
|
uint64_t offset = 0;
|
|
|
|
|
uint64_t size = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct hang_map {
|
|
|
|
|
uint64_t offset = 0;
|
|
|
|
|
uint64_t size = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct hang_exec {
|
|
|
|
|
uint64_t offset = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* UI */
|
|
|
|
|
|
|
|
|
|
#include <epoxy/gl.h>
|
|
|
|
|
|
|
|
|
|
#include "imgui/imgui.h"
|
|
|
|
|
#include "imgui/imgui_memory_editor.h"
|
|
|
|
|
#include "imgui_impl_gtk3.h"
|
|
|
|
|
#include "imgui_impl_opengl3.h"
|
|
|
|
|
|
|
|
|
|
#include "aubinator_viewer.h"
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class window {
|
|
|
|
|
public:
|
|
|
|
|
virtual void display() = 0;
|
|
|
|
|
virtual void destroy() = 0;
|
|
|
|
|
|
|
|
|
|
virtual ~window() {}
|
|
|
|
|
|
|
|
|
|
const char *name() const { return m_name; }
|
|
|
|
|
|
|
|
|
|
bool m_opened = true;
|
|
|
|
|
|
|
|
|
|
ImVec2 m_position = ImVec2(-1, -1);
|
|
|
|
|
ImVec2 m_size = ImVec2(700, 300);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
window() {}
|
|
|
|
|
|
|
|
|
|
char m_name[128];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct Context {
|
|
|
|
|
/* Hang file descriptor */
|
|
|
|
|
int file_fd = -1;
|
|
|
|
|
void *file_map = NULL;
|
|
|
|
|
|
|
|
|
|
/* Map hang file in RW for edition */
|
|
|
|
|
bool edit = false;
|
|
|
|
|
|
2024-01-14 10:57:29 +02:00
|
|
|
/* AUX-TT */
|
|
|
|
|
uint64_t aux_tt_addr = 0;
|
|
|
|
|
|
2023-05-02 16:41:33 +03:00
|
|
|
struct intel_device_info devinfo;
|
|
|
|
|
struct intel_spec *spec = NULL;
|
|
|
|
|
struct brw_isa_info isa;
|
|
|
|
|
|
|
|
|
|
/* Result of parsing the hang file */
|
|
|
|
|
std::vector<hang_bo> bos;
|
|
|
|
|
std::vector<hang_map> maps;
|
|
|
|
|
std::vector<hang_exec> execs;
|
|
|
|
|
|
|
|
|
|
hang_bo hw_image;
|
|
|
|
|
|
|
|
|
|
GtkWidget *gtk_window;
|
|
|
|
|
|
|
|
|
|
/* UI state*/
|
|
|
|
|
bool show_commands_window;
|
|
|
|
|
bool show_registers_window;
|
|
|
|
|
|
|
|
|
|
struct aub_viewer_cfg cfg;
|
|
|
|
|
|
|
|
|
|
std::vector<std::shared_ptr<window>> windows;
|
|
|
|
|
} context;
|
|
|
|
|
|
|
|
|
|
thread_local ImGuiContext* __MesaImGui;
|
|
|
|
|
|
2024-01-14 10:57:29 +02:00
|
|
|
hang_bo *find_bo(uint64_t addr)
|
|
|
|
|
{
|
|
|
|
|
for (auto &bo : context.bos) {
|
|
|
|
|
if (addr >= bo.offset && addr < (bo.offset + bo.size))
|
|
|
|
|
return &bo;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-02 16:41:33 +03:00
|
|
|
/**/
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
read_edit_window(const uint8_t *data, size_t off)
|
|
|
|
|
{
|
|
|
|
|
return data[off];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_edit_window(uint8_t *data, size_t off, uint8_t d)
|
|
|
|
|
{
|
|
|
|
|
data[off] = d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class edit_window : public window {
|
|
|
|
|
public:
|
|
|
|
|
struct hang_bo m_bo;
|
|
|
|
|
|
|
|
|
|
struct intel_batch_decode_bo m_aub_bo;
|
|
|
|
|
uint64_t m_aub_offset;
|
|
|
|
|
|
|
|
|
|
struct intel_batch_decode_bo m_gtt_bo;
|
|
|
|
|
uint64_t m_gtt_offset;
|
|
|
|
|
|
|
|
|
|
struct MemoryEditor m_editor;
|
|
|
|
|
|
|
|
|
|
edit_window(const struct hang_bo &bo)
|
|
|
|
|
: m_bo(bo) {
|
|
|
|
|
m_editor.OptShowDataPreview = true;
|
|
|
|
|
m_editor.OptShowAscii = false;
|
|
|
|
|
m_editor.ReadFn = read_edit_window;
|
|
|
|
|
m_editor.WriteFn = write_edit_window;
|
|
|
|
|
|
|
|
|
|
snprintf(m_name, sizeof(m_name), "Memory view 0x%016" PRIx64 "##%p",
|
|
|
|
|
bo.offset, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display() {
|
|
|
|
|
if (m_bo.map) {
|
|
|
|
|
ImGui::BeginChild(ImGui::GetID("##block"));
|
|
|
|
|
m_editor.DrawContents((uint8_t *) m_bo.map, m_bo.size, m_bo.offset);
|
|
|
|
|
ImGui::EndChild();
|
|
|
|
|
} else {
|
|
|
|
|
ImGui::Text("Memory view at 0x%" PRIx64 " not available", m_bo.offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destroy() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class shader_window : public window {
|
|
|
|
|
public:
|
|
|
|
|
std::string m_description;
|
|
|
|
|
uint64_t m_address;
|
|
|
|
|
std::string m_shader;
|
|
|
|
|
|
|
|
|
|
shader_window(const char *description, uint64_t address)
|
|
|
|
|
: m_description(description)
|
|
|
|
|
, m_address(address) {
|
|
|
|
|
snprintf(m_name, sizeof(m_name),
|
|
|
|
|
"%s (0x%" PRIx64 ")##%p", m_description.c_str(), m_address, this);
|
|
|
|
|
|
2024-01-14 10:57:29 +02:00
|
|
|
hang_bo *bo = find_bo(address);
|
|
|
|
|
if (bo != NULL) {
|
|
|
|
|
char *shader_txt = NULL;
|
|
|
|
|
size_t shader_txt_size = 0;
|
|
|
|
|
FILE *f = open_memstream(&shader_txt, &shader_txt_size);
|
|
|
|
|
if (f) {
|
2024-01-24 23:12:52 -08:00
|
|
|
brw_disassemble_with_errors(&context.isa,
|
|
|
|
|
(const uint8_t *) bo->map +
|
|
|
|
|
(address - bo->offset), 0, f);
|
2024-01-14 10:57:29 +02:00
|
|
|
fclose(f);
|
2023-05-02 16:41:33 +03:00
|
|
|
}
|
2024-01-14 10:57:29 +02:00
|
|
|
|
|
|
|
|
m_shader = std::string(shader_txt);
|
2023-05-02 16:41:33 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display() {
|
|
|
|
|
ImGui::InputTextMultiline("Assembly",
|
|
|
|
|
(char *) m_shader.c_str(), m_shader.size(),
|
|
|
|
|
ImGui::GetContentRegionAvail(),
|
|
|
|
|
ImGuiInputTextFlags_ReadOnly);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destroy() {}
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-14 10:57:29 +02:00
|
|
|
class aux_tt_window : public window {
|
|
|
|
|
public:
|
|
|
|
|
aux_tt_window(uint64_t l3_addr)
|
|
|
|
|
: m_l3_addr(l3_addr)
|
|
|
|
|
, m_bo(find_bo(l3_addr)) {
|
|
|
|
|
snprintf(m_name, sizeof(m_name), "AUX TT##%p", this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display() {
|
|
|
|
|
ImGui::BeginChild(ImGui::GetID("##toplevel"));
|
|
|
|
|
if (m_bo != NULL)
|
|
|
|
|
display_level(3, m_l3_addr, 0);
|
|
|
|
|
else
|
|
|
|
|
ImGui::Text("AUX table buffer not found: 0x%" PRIx64,
|
|
|
|
|
m_l3_addr);
|
|
|
|
|
ImGui::EndChild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destroy() {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void display_level(int level, uint64_t table_addr, uint64_t base_addr) {
|
|
|
|
|
assert(level >= 1 && level <= 3);
|
|
|
|
|
|
|
|
|
|
const hang_bo *bo =
|
|
|
|
|
table_addr == m_l3_addr ? m_bo : find_bo(table_addr);
|
|
|
|
|
if (bo == NULL) {
|
|
|
|
|
ImGui::Text("level %u not found addr=0x%016" PRIx64,
|
|
|
|
|
level, table_addr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
uint32_t top;
|
|
|
|
|
uint32_t bottom;
|
|
|
|
|
} levels[4] = {
|
|
|
|
|
{ 0, 0, },
|
|
|
|
|
{ 23, 16, },
|
|
|
|
|
{ 35, 24, },
|
|
|
|
|
{ 47, 36, },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uint64_t *entries =
|
|
|
|
|
(const uint64_t *)((const uint8_t *)bo->map + (table_addr - bo->offset));
|
|
|
|
|
|
|
|
|
|
if (level == 1) {
|
|
|
|
|
uint32_t n_entries = context.devinfo.verx10 == 125 ? 16 : 256;
|
|
|
|
|
for (uint32_t i = 0; i < n_entries; i++) {
|
|
|
|
|
uint64_t addr = entries[i] & 0xffffffffff00ull;
|
|
|
|
|
ImGui::Text("entry%04u: addr=0x%012" PRIx64 " entry=0x%012" PRIx64
|
|
|
|
|
" range=0x%012" PRIx64 "-0x%012" PRIx64,
|
|
|
|
|
i, addr, entries[i],
|
|
|
|
|
base_addr + (uint64_t)i << levels[level].bottom,
|
|
|
|
|
base_addr + (uint64_t)i << levels[level].bottom);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (uint32_t i = 0; i < 4096; i++) {
|
|
|
|
|
uint64_t entry_addr = base_addr + (uint64_t)i << levels[level].bottom;
|
|
|
|
|
uint64_t addr = entries[i] & 0xffffffff8000ull;
|
|
|
|
|
bool valid = (entries[i] & 0x1) != 0;
|
|
|
|
|
if (valid &&
|
|
|
|
|
ImGui::TreeNodeEx(
|
|
|
|
|
(void *)&entries[i],
|
|
|
|
|
ImGuiTreeNodeFlags_Framed,
|
|
|
|
|
"entry%04u: addr=0x%012" PRIx64 " entry=0x%012" PRIx64
|
|
|
|
|
" range=0x%012" PRIx64 "-0x%012" PRIx64,
|
|
|
|
|
i, addr, entries[i],
|
|
|
|
|
entry_addr, entry_addr)) {
|
|
|
|
|
if (valid)
|
|
|
|
|
display_level(level - 1, addr, entry_addr);
|
|
|
|
|
ImGui::TreePop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t m_l3_addr;
|
|
|
|
|
const hang_bo *m_bo;
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-02 16:41:33 +03:00
|
|
|
static struct intel_batch_decode_bo
|
|
|
|
|
batch_get_bo(void *user_data, bool ppgtt, uint64_t address)
|
|
|
|
|
{
|
|
|
|
|
intel_batch_decode_bo ret_bo;
|
|
|
|
|
ret_bo.map = NULL;
|
|
|
|
|
ret_bo.addr = 0;
|
|
|
|
|
|
|
|
|
|
if (!ppgtt)
|
|
|
|
|
return ret_bo;
|
|
|
|
|
|
|
|
|
|
for (const auto &bo : context.bos) {
|
|
|
|
|
if (address >= bo.offset &&
|
|
|
|
|
address < (bo.offset + bo.size)) {
|
|
|
|
|
ret_bo.map = bo.map;
|
|
|
|
|
ret_bo.addr = bo.offset;
|
|
|
|
|
ret_bo.size = bo.size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret_bo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)
|
|
|
|
|
{
|
|
|
|
|
context.windows.push_back(std::shared_ptr<window>(new shader_window(shader_desc, address)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class batch_window : public window {
|
|
|
|
|
public:
|
|
|
|
|
batch_window(const struct hang_bo &bo)
|
|
|
|
|
: m_bo(bo)
|
|
|
|
|
, m_collapsed(true) {
|
|
|
|
|
aub_viewer_decode_ctx_init(&m_decode_ctx,
|
|
|
|
|
&context.cfg,
|
|
|
|
|
&m_decode_cfg,
|
|
|
|
|
&context.devinfo,
|
|
|
|
|
context.spec,
|
|
|
|
|
batch_get_bo,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
m_decode_ctx.display_shader = batch_display_shader;
|
|
|
|
|
// window->decode_ctx.display_urb = batch_display_urb;
|
|
|
|
|
// window->decode_ctx.edit_address = batch_edit_address;
|
|
|
|
|
|
|
|
|
|
snprintf(m_name, sizeof(m_name), "Batch view 0x%016" PRIx64 "##%p",
|
|
|
|
|
bo.offset, this);
|
|
|
|
|
}
|
|
|
|
|
~batch_window() {}
|
|
|
|
|
|
|
|
|
|
void display() {
|
|
|
|
|
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
|
|
|
|
|
decode_options();
|
|
|
|
|
if (ImGui::Button("Edit commands"))
|
|
|
|
|
context.windows.push_back(std::shared_ptr<window>(new edit_window(m_bo)));
|
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
|
|
|
|
|
|
ImGui::BeginChild(ImGui::GetID("##block"));
|
|
|
|
|
|
|
|
|
|
aub_viewer_render_batch(&m_decode_ctx,
|
|
|
|
|
m_bo.map,
|
|
|
|
|
m_bo.size,
|
|
|
|
|
m_bo.offset,
|
|
|
|
|
false /* from_ring */);
|
|
|
|
|
|
|
|
|
|
ImGui::EndChild();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void destroy() {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
void decode_options() {
|
|
|
|
|
char name[40];
|
|
|
|
|
snprintf(name, sizeof(name), "command filter##%p", &m_decode_cfg.command_filter);
|
|
|
|
|
m_decode_cfg.command_filter.Draw(name); ImGui::SameLine();
|
|
|
|
|
snprintf(name, sizeof(name), "field filter##%p", &m_decode_cfg.field_filter);
|
|
|
|
|
m_decode_cfg.field_filter.Draw(name); ImGui::SameLine();
|
|
|
|
|
if (ImGui::Button("Dwords")) m_decode_cfg.show_dwords ^= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct hang_bo m_bo;
|
|
|
|
|
|
|
|
|
|
bool m_collapsed;
|
|
|
|
|
|
|
|
|
|
struct aub_viewer_decode_cfg m_decode_cfg;
|
|
|
|
|
struct aub_viewer_decode_ctx m_decode_ctx;
|
|
|
|
|
|
|
|
|
|
char edit_address[20];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* 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_hang_stats()
|
|
|
|
|
{
|
|
|
|
|
ImGui::Begin("Hang stats");
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();
|
|
|
|
|
|
|
|
|
|
if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
|
|
|
|
|
|
2023-12-07 12:52:37 -08:00
|
|
|
ImGui::Text("BOs: %zu", context.bos.size());
|
|
|
|
|
ImGui::Text("Execs %zu", context.execs.size());
|
|
|
|
|
ImGui::Text("Maps: %zu", context.maps.size());
|
2023-05-02 16:41:33 +03:00
|
|
|
ImGui::Text("PCI ID: 0x%x", context.devinfo.pci_device_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("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
|
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t exec_buf_addr = 0;
|
|
|
|
|
if (!context.execs.empty())
|
|
|
|
|
exec_buf_addr = context.execs.front().offset;
|
|
|
|
|
|
|
|
|
|
ImGui::BeginChild(ImGui::GetID("BO list:"));
|
|
|
|
|
for (const auto &bo : context.bos) {
|
|
|
|
|
char bo_name[80];
|
2024-01-14 10:57:29 +02:00
|
|
|
snprintf(bo_name, sizeof(bo_name),
|
|
|
|
|
"BO 0x%012" PRIx64 "-0x%012" PRIx64 " size=%" PRIu64 "(%s) %s",
|
|
|
|
|
bo.offset, bo.offset + bo.size - 1, bo.size, human_size(bo.size),
|
2023-05-02 16:41:33 +03:00
|
|
|
bo.offset == exec_buf_addr ? "BATCH BUFFER" : "");
|
|
|
|
|
|
|
|
|
|
if (ImGui::Selectable(bo_name, false))
|
|
|
|
|
context.windows.push_back(std::shared_ptr<window>(new batch_window(bo)));
|
|
|
|
|
}
|
|
|
|
|
if (context.hw_image.size != 0 && ImGui::Selectable("HW IMAGE", false))
|
|
|
|
|
context.windows.push_back(std::shared_ptr<window>(new batch_window(context.hw_image)));
|
2024-01-14 10:57:29 +02:00
|
|
|
if (context.aux_tt_addr != 0 && ImGui::Selectable("AUX-TT", false))
|
|
|
|
|
context.windows.push_back(std::shared_ptr<window>(new aux_tt_window(context.aux_tt_addr)));
|
2023-05-02 16:41:33 +03:00
|
|
|
ImGui::EndChild();
|
|
|
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Main redrawing */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
display_windows(void)
|
|
|
|
|
{
|
|
|
|
|
display_hang_stats();
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
|
|
|
|
auto it = context.windows.begin();
|
|
|
|
|
while (it != context.windows.end()) {
|
|
|
|
|
if (!(*it)->m_opened) {
|
|
|
|
|
(*it)->destroy();
|
|
|
|
|
it = context.windows.erase(it);
|
|
|
|
|
} else {
|
|
|
|
|
it++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < context.windows.size(); i++) {
|
|
|
|
|
std::shared_ptr<window> window = context.windows[i];
|
|
|
|
|
ImGui::SetNextWindowPos(window->m_position, ImGuiCond_FirstUseEver);
|
|
|
|
|
ImGui::SetNextWindowSize(window->m_size, ImGuiCond_FirstUseEver);
|
|
|
|
|
if (ImGui::Begin(window->name(), &window->m_opened)) {
|
|
|
|
|
window->display();
|
|
|
|
|
window->m_position = ImGui::GetWindowPos();
|
|
|
|
|
window->m_size = ImGui::GetWindowSize();
|
|
|
|
|
}
|
|
|
|
|
if (window_has_ctrl_key('w'))
|
|
|
|
|
window->m_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");
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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);
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_help(const char *progname, FILE *file)
|
|
|
|
|
{
|
|
|
|
|
fprintf(file,
|
|
|
|
|
"Usage: %s -p platform HANG_FILE\n"
|
|
|
|
|
"\n"
|
|
|
|
|
" -p, --platform platform platform to use for decoding\n"
|
|
|
|
|
" -e, --edit map the hang file read/write for edition\n"
|
2024-01-14 10:57:29 +02:00
|
|
|
" -x, --aux-tt map the hang file read/write for edition\n"
|
2023-05-02 16:41:33 +03:00
|
|
|
, progname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_bo(void *map, uint64_t addr, uint64_t size)
|
|
|
|
|
{
|
|
|
|
|
hang_bo bo;
|
|
|
|
|
bo.map = map;
|
|
|
|
|
bo.offset = addr;
|
|
|
|
|
bo.size = size;
|
|
|
|
|
context.bos.push_back(bo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_map(uint64_t addr, uint64_t size)
|
|
|
|
|
{
|
|
|
|
|
hang_map map;
|
|
|
|
|
map.offset = addr;
|
|
|
|
|
map.size = size;
|
|
|
|
|
context.maps.push_back(map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
add_exec(uint64_t addr)
|
|
|
|
|
{
|
|
|
|
|
hang_exec exec;
|
|
|
|
|
exec.offset = addr;
|
|
|
|
|
context.execs.push_back(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
get_block_size(uint32_t type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_HEADER: return sizeof(struct intel_hang_dump_block_header);
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_BO: return sizeof(struct intel_hang_dump_block_bo);
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_MAP: return sizeof(struct intel_hang_dump_block_map);
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_EXEC: return sizeof(struct intel_hang_dump_block_exec);
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE: return sizeof(struct intel_hang_dump_block_hw_image);
|
|
|
|
|
default: unreachable("invalid block");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
parse_hang_file(const char *filename)
|
|
|
|
|
{
|
|
|
|
|
context.file_fd = open(filename, context.edit ? O_RDWR : O_RDONLY);
|
|
|
|
|
if (context.file_fd < 0)
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
struct stat file_stats;
|
|
|
|
|
if (fstat(context.file_fd, &file_stats) != 0)
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
context.file_map = mmap(NULL, file_stats.st_size,
|
|
|
|
|
PROT_READ | PROT_WRITE,
|
|
|
|
|
context.edit ? MAP_SHARED : MAP_PRIVATE,
|
|
|
|
|
context.file_fd, 0);
|
|
|
|
|
if (context.file_map == MAP_FAILED)
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
uint8_t *current_file_ptr = (uint8_t *) context.file_map;
|
|
|
|
|
uint8_t *last_file_ptr = current_file_ptr + file_stats.st_size;
|
|
|
|
|
|
|
|
|
|
while (current_file_ptr < last_file_ptr) {
|
|
|
|
|
union intel_hang_dump_block_all *block_header =
|
|
|
|
|
(union intel_hang_dump_block_all *)current_file_ptr;
|
|
|
|
|
size_t block_size = get_block_size(block_header->base.type);
|
|
|
|
|
|
|
|
|
|
switch (block_header->base.type) {
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_HEADER:
|
|
|
|
|
assert(block_header->header.magic == INTEL_HANG_DUMP_MAGIC);
|
|
|
|
|
assert(block_header->header.version == INTEL_HANG_DUMP_VERSION);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_BO: {
|
|
|
|
|
add_bo((uint8_t *) current_file_ptr + block_size,
|
|
|
|
|
block_header->bo.offset,
|
|
|
|
|
block_header->bo.size);
|
|
|
|
|
current_file_ptr = (uint8_t *) current_file_ptr + block_size + block_header->bo.size;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE: {
|
|
|
|
|
context.hw_image.offset = block_header->bo.offset;
|
|
|
|
|
context.hw_image.size = block_header->hw_img.size;
|
|
|
|
|
context.hw_image.map = (uint8_t *) current_file_ptr + block_size;
|
|
|
|
|
current_file_ptr = (uint8_t *) current_file_ptr + block_size + block_header->hw_img.size;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_MAP: {
|
|
|
|
|
add_map(block_header->map.offset,
|
|
|
|
|
block_header->map.size);
|
|
|
|
|
current_file_ptr = (uint8_t *) current_file_ptr + block_size;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case INTEL_HANG_DUMP_BLOCK_TYPE_EXEC: {
|
|
|
|
|
add_exec(block_header->exec.offset);
|
|
|
|
|
current_file_ptr = (uint8_t *) current_file_ptr + block_size;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
unreachable("Invalid block type");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
int c, i;
|
2024-01-14 10:57:29 +02:00
|
|
|
bool help = false;
|
2023-05-02 16:41:33 +03:00
|
|
|
const char *platform = NULL;
|
|
|
|
|
const struct option aubinator_opts[] = {
|
2024-01-14 10:57:29 +02:00
|
|
|
{ "platform", required_argument, NULL, 'p' },
|
|
|
|
|
{ "aux-tt", required_argument, NULL, 'x' },
|
|
|
|
|
{ "edit", no_argument, NULL, 'e' },
|
2023-05-02 16:41:33 +03:00
|
|
|
{ "help", no_argument, (int *) &help, true },
|
|
|
|
|
{ NULL, 0, NULL, 0 },
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-14 10:57:29 +02:00
|
|
|
context = {};
|
|
|
|
|
|
2023-05-02 16:41:33 +03:00
|
|
|
i = 0;
|
2024-01-14 10:57:29 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "p:ex:", aubinator_opts, &i)) != -1) {
|
2023-05-02 16:41:33 +03:00
|
|
|
switch (c) {
|
|
|
|
|
case 'p':
|
|
|
|
|
platform = optarg;
|
|
|
|
|
break;
|
|
|
|
|
case 'e':
|
2024-01-14 10:57:29 +02:00
|
|
|
context.edit = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
context.aux_tt_addr = strtoll(optarg, NULL, 16);
|
2023-05-02 16:41:33 +03:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *filename = NULL;
|
|
|
|
|
if (optind < argc)
|
|
|
|
|
filename = argv[optind];
|
|
|
|
|
|
|
|
|
|
if (help || !platform || !filename) {
|
|
|
|
|
print_help(argv[0], stderr);
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
intel_get_device_info_from_pci_id(
|
|
|
|
|
intel_device_name_to_pci_device_id(platform),
|
|
|
|
|
&context.devinfo);
|
|
|
|
|
|
|
|
|
|
brw_init_isa_info(&context.isa, &context.devinfo);
|
|
|
|
|
context.spec = intel_spec_load(&context.devinfo);
|
|
|
|
|
|
|
|
|
|
parse_hang_file(filename);
|
|
|
|
|
|
|
|
|
|
gtk_init(NULL, NULL);
|
|
|
|
|
|
|
|
|
|
context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
|
|
|
gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Hang 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);
|
|
|
|
|
g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);
|
|
|
|
|
gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
|
|
|
|
|
|
|
|
|
|
gtk_widget_show_all(context.gtk_window);
|
|
|
|
|
|
|
|
|
|
gtk_main();
|
|
|
|
|
|
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
|
}
|