1/* 2 * Copyright © 2016 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <stdint.h> 27#include <getopt.h> 28#include <unistd.h> 29#include <fcntl.h> 30#include <string.h> 31#include <errno.h> 32#include <sys/stat.h> 33#include <sys/mman.h> 34#include <sys/types.h> 35#include <ctype.h> 36 37#include "util/macros.h" 38 39#include "aub_read.h" 40#include "aub_mem.h" 41 42#include "common/gen_disasm.h" 43 44#define xtzalloc(name) ((decltype(&name)) calloc(1, sizeof(name))) 45#define xtalloc(name) ((decltype(&name)) malloc(sizeof(name))) 46 47struct aub_file { 48 uint8_t *map, *end, *cursor; 49 50 uint16_t pci_id; 51 char app_name[33]; 52 53 /* List of batch buffers to process */ 54 struct { 55 const uint8_t *start; 56 const uint8_t *end; 57 } *execs; 58 int n_execs; 59 int n_allocated_execs; 60 61 uint32_t idx_reg_write; 62 63 /* Device state */ 64 struct gen_device_info devinfo; 65 struct gen_spec *spec; 66 struct gen_disasm *disasm; 67}; 68 69static void 70store_exec_begin(struct aub_file *file) 71{ 72 if (unlikely(file->n_execs >= file->n_allocated_execs)) { 73 file->n_allocated_execs = MAX2(2U * file->n_allocated_execs, 74 4096 / sizeof(file->execs[0])); 75 file->execs = (decltype(file->execs)) 76 realloc(static_cast<void *>(file->execs), 77 file->n_allocated_execs * sizeof(file->execs[0])); 78 } 79 80 file->execs[file->n_execs++].start = file->cursor; 81} 82 83static void 84store_exec_end(struct aub_file *file) 85{ 86 if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL) 87 file->execs[file->n_execs - 1].end = file->cursor; 88} 89 90static void 91handle_mem_write(void *user_data, uint64_t phys_addr, 92 const void *data, uint32_t data_len) 93{ 94 struct aub_file *file = (struct aub_file *) user_data; 95 file->idx_reg_write = 0; 96 store_exec_end(file); 97} 98 99static void 100handle_ring_write(void *user_data, enum drm_i915_gem_engine_class engine, 101 const void *ring_data, uint32_t ring_data_len) 102{ 103 struct aub_file *file = (struct aub_file *) user_data; 104 file->idx_reg_write = 0; 105 store_exec_begin(file); 106} 107 108static void 109handle_reg_write(void *user_data, uint32_t reg_offset, uint32_t reg_value) 110{ 111 struct aub_file *file = (struct aub_file *) user_data; 112 113 /* Only store the first register write of a series (execlist writes take 114 * involve 2 dwords). 115 */ 116 if (file->idx_reg_write++ == 0) 117 store_exec_begin(file); 118} 119 120static void 121handle_info(void *user_data, int pci_id, const char *app_name) 122{ 123 struct aub_file *file = (struct aub_file *) user_data; 124 store_exec_end(file); 125 126 file->pci_id = pci_id; 127 snprintf(file->app_name, sizeof(app_name), "%s", app_name); 128 129 if (!gen_get_device_info(file->pci_id, &file->devinfo)) { 130 fprintf(stderr, "can't find device information: pci_id=0x%x\n", file->pci_id); 131 exit(EXIT_FAILURE); 132 } 133 file->spec = gen_spec_load(&file->devinfo); 134 file->disasm = gen_disasm_create(&file->devinfo); 135} 136 137static void 138handle_error(void *user_data, const void *aub_data, const char *msg) 139{ 140 fprintf(stderr, "ERROR: %s\n", msg); 141} 142 143static struct aub_file * 144aub_file_open(const char *filename) 145{ 146 struct aub_file *file; 147 struct stat sb; 148 int fd; 149 150 file = xtzalloc(*file); 151 fd = open(filename, O_RDWR); 152 if (fd == -1) { 153 fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno)); 154 exit(EXIT_FAILURE); 155 } 156 157 if (fstat(fd, &sb) == -1) { 158 fprintf(stderr, "stat failed: %s\n", strerror(errno)); 159 exit(EXIT_FAILURE); 160 } 161 162 file->map = (uint8_t *) mmap(NULL, sb.st_size, 163 PROT_READ, MAP_SHARED, fd, 0); 164 if (file->map == MAP_FAILED) { 165 fprintf(stderr, "mmap failed: %s\n", strerror(errno)); 166 exit(EXIT_FAILURE); 167 } 168 169 close(fd); 170 171 file->cursor = file->map; 172 file->end = file->map + sb.st_size; 173 174 struct aub_read aub_read = {}; 175 aub_read.user_data = file; 176 aub_read.info = handle_info; 177 aub_read.error = handle_error; 178 aub_read.reg_write = handle_reg_write; 179 aub_read.ring_write = handle_ring_write; 180 aub_read.local_write = handle_mem_write; 181 aub_read.phys_write = handle_mem_write; 182 aub_read.ggtt_write = handle_mem_write; 183 aub_read.ggtt_entry_write = handle_mem_write; 184 185 int consumed; 186 while (file->cursor < file->end && 187 (consumed = aub_read_command(&aub_read, file->cursor, 188 file->end - file->cursor)) > 0) { 189 file->cursor += consumed; 190 } 191 192 /* Ensure we have an end on the last register write. */ 193 if (file->n_execs > 0 && file->execs[file->n_execs - 1].end == NULL) 194 file->execs[file->n_execs - 1].end = file->end; 195 196 return file; 197} 198 199/**/ 200 201static void 202update_mem_for_exec(struct aub_mem *mem, struct aub_file *file, int exec_idx) 203{ 204 struct aub_read read = {}; 205 read.user_data = mem; 206 read.local_write = aub_mem_local_write; 207 read.phys_write = aub_mem_phys_write; 208 read.ggtt_write = aub_mem_ggtt_write; 209 read.ggtt_entry_write = aub_mem_ggtt_entry_write; 210 211 /* Replay the aub file from the beginning up to just before the 212 * commands we want to read. where the context setup happens. 213 */ 214 const uint8_t *iter = file->map; 215 while (iter < file->execs[exec_idx].start) { 216 iter += aub_read_command(&read, iter, file->execs[exec_idx].start - iter); 217 } 218} 219 220/* UI */ 221 222#include <epoxy/gl.h> 223 224#include "imgui/imgui.h" 225#include "imgui/imgui_memory_editor.h" 226#include "imgui_impl_gtk3.h" 227#include "imgui_impl_opengl3.h" 228 229#include "aubinator_viewer.h" 230#include "aubinator_viewer_urb.h" 231 232struct window { 233 struct list_head link; /* link in the global list of windows */ 234 struct list_head parent_link; /* link in parent window list of children */ 235 236 struct list_head children_windows; /* list of children windows */ 237 238 char name[128]; 239 bool opened; 240 241 ImVec2 position; 242 ImVec2 size; 243 244 void (*display)(struct window*); 245 void (*destroy)(struct window*); 246}; 247 248struct edit_window { 249 struct window base; 250 251 struct aub_mem *mem; 252 uint64_t address; 253 uint32_t len; 254 255 struct gen_batch_decode_bo aub_bo; 256 uint64_t aub_offset; 257 258 struct gen_batch_decode_bo gtt_bo; 259 uint64_t gtt_offset; 260 261 struct MemoryEditor editor; 262}; 263 264struct pml4_window { 265 struct window base; 266 267 struct aub_mem *mem; 268}; 269 270struct shader_window { 271 struct window base; 272 273 uint64_t address; 274 char *shader; 275 size_t shader_size; 276}; 277 278struct urb_window { 279 struct window base; 280 281 uint32_t end_urb_offset; 282 struct aub_decode_urb_stage_state urb_stages[AUB_DECODE_N_STAGE]; 283 284 AubinatorViewerUrb urb_view; 285}; 286 287struct batch_window { 288 struct window base; 289 290 struct aub_mem mem; 291 struct aub_read read; 292 293 bool uses_ppgtt; 294 295 bool collapsed; 296 int exec_idx; 297 298 struct aub_viewer_decode_cfg decode_cfg; 299 struct aub_viewer_decode_ctx decode_ctx; 300 301 struct pml4_window pml4_window; 302 303 char edit_address[20]; 304}; 305 306static struct Context { 307 struct aub_file *file; 308 char *input_file; 309 char *xml_path; 310 311 GtkWidget *gtk_window; 312 313 /* UI state*/ 314 bool show_commands_window; 315 bool show_registers_window; 316 317 struct aub_viewer_cfg cfg; 318 319 struct list_head windows; 320 321 struct window file_window; 322 struct window commands_window; 323 struct window registers_window; 324} context; 325 326thread_local ImGuiContext* __MesaImGui; 327 328static int 329map_key(int k) 330{ 331 return ImGuiKey_COUNT + k; 332} 333 334static bool 335has_ctrl_key(int key) 336{ 337 return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key)); 338} 339 340static bool 341window_has_ctrl_key(int key) 342{ 343 return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key); 344} 345 346static void 347destroy_window_noop(struct window *win) 348{ 349} 350 351/* Shader windows */ 352 353static void 354display_shader_window(struct window *win) 355{ 356 struct shader_window *window = (struct shader_window *) win; 357 358 if (window->shader) { 359 ImGui::InputTextMultiline("Assembly", 360 window->shader, window->shader_size, 361 ImGui::GetContentRegionAvail(), 362 ImGuiInputTextFlags_ReadOnly); 363 } else { 364 ImGui::Text("Shader not available"); 365 } 366} 367 368static void 369destroy_shader_window(struct window *win) 370{ 371 struct shader_window *window = (struct shader_window *) win; 372 373 free(window->shader); 374 free(window); 375} 376 377static struct shader_window * 378new_shader_window(struct aub_mem *mem, uint64_t address, const char *desc) 379{ 380 struct shader_window *window = xtzalloc(*window); 381 382 snprintf(window->base.name, sizeof(window->base.name), 383 "%s (0x%lx)##%p", desc, address, window); 384 385 list_inithead(&window->base.parent_link); 386 window->base.position = ImVec2(-1, -1); 387 window->base.size = ImVec2(700, 300); 388 window->base.opened = true; 389 window->base.display = display_shader_window; 390 window->base.destroy = destroy_shader_window; 391 392 struct gen_batch_decode_bo shader_bo = 393 aub_mem_get_ppgtt_bo(mem, address); 394 if (shader_bo.map) { 395 FILE *f = open_memstream(&window->shader, &window->shader_size); 396 if (f) { 397 gen_disasm_disassemble(context.file->disasm, 398 (const uint8_t *) shader_bo.map + 399 (address - shader_bo.addr), 0, f); 400 fclose(f); 401 } 402 } 403 404 list_addtail(&window->base.link, &context.windows); 405 406 return window; 407} 408 409/* URB windows */ 410 411static void 412display_urb_window(struct window *win) 413{ 414 struct urb_window *window = (struct urb_window *) win; 415 static const char *stages[] = { 416 [AUB_DECODE_STAGE_VS] = "VS", 417 [AUB_DECODE_STAGE_HS] = "HS", 418 [AUB_DECODE_STAGE_DS] = "DS", 419 [AUB_DECODE_STAGE_GS] = "GS", 420 [AUB_DECODE_STAGE_PS] = "PS", 421 [AUB_DECODE_STAGE_CS] = "CS", 422 }; 423 424 ImGui::Text("URB allocation:"); 425 window->urb_view.DrawAllocation("##urb", 426 ARRAY_SIZE(window->urb_stages), 427 window->end_urb_offset, 428 stages, 429 &window->urb_stages[0]); 430} 431 432static void 433destroy_urb_window(struct window *win) 434{ 435 struct urb_window *window = (struct urb_window *) win; 436 437 free(window); 438} 439 440static struct urb_window * 441new_urb_window(struct aub_viewer_decode_ctx *decode_ctx, uint64_t address) 442{ 443 struct urb_window *window = xtzalloc(*window); 444 445 snprintf(window->base.name, sizeof(window->base.name), 446 "URB view (0x%lx)##%p", address, window); 447 448 list_inithead(&window->base.parent_link); 449 window->base.position = ImVec2(-1, -1); 450 window->base.size = ImVec2(700, 300); 451 window->base.opened = true; 452 window->base.display = display_urb_window; 453 window->base.destroy = destroy_urb_window; 454 455 window->end_urb_offset = decode_ctx->end_urb_offset; 456 memcpy(window->urb_stages, decode_ctx->urb_stages, sizeof(window->urb_stages)); 457 window->urb_view = AubinatorViewerUrb(); 458 459 list_addtail(&window->base.link, &context.windows); 460 461 return window; 462} 463 464/* Memory editor windows */ 465 466static uint8_t 467read_edit_window(const uint8_t *data, size_t off) 468{ 469 struct edit_window *window = (struct edit_window *) data; 470 471 return *((const uint8_t *) window->gtt_bo.map + window->gtt_offset + off); 472} 473 474static void 475write_edit_window(uint8_t *data, size_t off, uint8_t d) 476{ 477 struct edit_window *window = (struct edit_window *) data; 478 uint8_t *gtt = (uint8_t *) window->gtt_bo.map + window->gtt_offset + off; 479 uint8_t *aub = (uint8_t *) window->aub_bo.map + window->aub_offset + off; 480 481 *gtt = *aub = d; 482} 483 484static void 485display_edit_window(struct window *win) 486{ 487 struct edit_window *window = (struct edit_window *) win; 488 489 if (window->aub_bo.map && window->gtt_bo.map) { 490 ImGui::BeginChild(ImGui::GetID("##block")); 491 window->editor.DrawContents((uint8_t *) window, 492 MIN3(window->len, 493 window->gtt_bo.size - window->gtt_offset, 494 window->aub_bo.size - window->aub_offset), 495 window->address); 496 ImGui::EndChild(); 497 } else { 498 ImGui::Text("Memory view at 0x%lx not available", window->address); 499 } 500} 501 502static void 503destroy_edit_window(struct window *win) 504{ 505 struct edit_window *window = (struct edit_window *) win; 506 507 if (window->aub_bo.map) 508 mprotect((void *) window->aub_bo.map, 4096, PROT_READ); 509 free(window); 510} 511 512static struct edit_window * 513new_edit_window(struct aub_mem *mem, uint64_t address, uint32_t len) 514{ 515 struct edit_window *window = xtzalloc(*window); 516 517 snprintf(window->base.name, sizeof(window->base.name), 518 "Editing aub at 0x%lx##%p", address, window); 519 520 list_inithead(&window->base.parent_link); 521 window->base.position = ImVec2(-1, -1); 522 window->base.size = ImVec2(500, 600); 523 window->base.opened = true; 524 window->base.display = display_edit_window; 525 window->base.destroy = destroy_edit_window; 526 527 window->mem = mem; 528 window->address = address; 529 window->aub_bo = aub_mem_get_ppgtt_addr_aub_data(mem, address); 530 window->gtt_bo = aub_mem_get_ppgtt_addr_data(mem, address); 531 window->len = len; 532 window->editor = MemoryEditor(); 533 window->editor.OptShowDataPreview = true; 534 window->editor.OptShowAscii = false; 535 window->editor.ReadFn = read_edit_window; 536 window->editor.WriteFn = write_edit_window; 537 538 if (window->aub_bo.map) { 539 uint64_t unaligned_map = (uint64_t) window->aub_bo.map; 540 window->aub_bo.map = (const void *)(unaligned_map & ~0xffful); 541 window->aub_offset = unaligned_map - (uint64_t) window->aub_bo.map; 542 543 if (mprotect((void *) window->aub_bo.map, window->aub_bo.size, PROT_READ | PROT_WRITE) != 0) { 544 window->aub_bo.map = NULL; 545 } 546 } 547 548 window->gtt_offset = address - window->gtt_bo.addr; 549 550 list_addtail(&window->base.link, &context.windows); 551 552 return window; 553} 554 555/* 4 level page table walk windows */ 556 557static void 558display_pml4_level(struct aub_mem *mem, uint64_t table_addr, uint64_t table_virt_addr, int level) 559{ 560 if (level == 0) 561 return; 562 563 struct gen_batch_decode_bo table_bo = 564 aub_mem_get_phys_addr_data(mem, table_addr); 565 const uint64_t *table = (const uint64_t *) ((const uint8_t *) table_bo.map + 566 table_addr - table_bo.addr); 567 if (!table) { 568 ImGui::TextColored(context.cfg.missing_color, "Page not available"); 569 return; 570 } 571 572 uint64_t addr_increment = 1ULL << (12 + 9 * (level - 1)); 573 574 if (level == 1) { 575 for (int e = 0; e < 512; e++) { 576 bool available = (table[e] & 1) != 0; 577 uint64_t entry_virt_addr = table_virt_addr + e * addr_increment; 578 if (!available) 579 continue; 580 ImGui::Text("Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx", 581 e, table[e], entry_virt_addr); 582 } 583 } else { 584 for (int e = 0; e < 512; e++) { 585 bool available = (table[e] & 1) != 0; 586 uint64_t entry_virt_addr = table_virt_addr + e * addr_increment; 587 if (available && 588 ImGui::TreeNodeEx(&table[e], 589 available ? ImGuiTreeNodeFlags_Framed : 0, 590 "Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx", 591 e, table[e], entry_virt_addr)) { 592 display_pml4_level(mem, table[e] & ~0xffful, entry_virt_addr, level -1); 593 ImGui::TreePop(); 594 } 595 } 596 } 597} 598 599static void 600display_pml4_window(struct window *win) 601{ 602 struct pml4_window *window = (struct pml4_window *) win; 603 604 ImGui::Text("pml4: %lx", window->mem->pml4); 605 ImGui::BeginChild(ImGui::GetID("##block")); 606 display_pml4_level(window->mem, window->mem->pml4, 0, 4); 607 ImGui::EndChild(); 608} 609 610static void 611show_pml4_window(struct pml4_window *window, struct aub_mem *mem) 612{ 613 if (window->base.opened) { 614 window->base.opened = false; 615 return; 616 } 617 618 snprintf(window->base.name, sizeof(window->base.name), 619 "4-Level page tables##%p", window); 620 621 list_inithead(&window->base.parent_link); 622 window->base.position = ImVec2(-1, -1); 623 window->base.size = ImVec2(500, 600); 624 window->base.opened = true; 625 window->base.display = display_pml4_window; 626 window->base.destroy = destroy_window_noop; 627 628 window->mem = mem; 629 630 list_addtail(&window->base.link, &context.windows); 631} 632 633/* Batch decoding windows */ 634 635static void 636display_decode_options(struct aub_viewer_decode_cfg *cfg) 637{ 638 char name[40]; 639 snprintf(name, sizeof(name), "command filter##%p", &cfg->command_filter); 640 cfg->command_filter.Draw(name); ImGui::SameLine(); 641 snprintf(name, sizeof(name), "field filter##%p", &cfg->field_filter); 642 cfg->field_filter.Draw(name); ImGui::SameLine(); 643 if (ImGui::Button("Dwords")) cfg->show_dwords ^= 1; 644} 645 646static void 647batch_display_shader(void *user_data, const char *shader_desc, uint64_t address) 648{ 649 struct batch_window *window = (struct batch_window *) user_data; 650 struct shader_window *shader_window = 651 new_shader_window(&window->mem, address, shader_desc); 652 653 list_add(&shader_window->base.parent_link, &window->base.children_windows); 654} 655 656static void 657batch_display_urb(void *user_data, const struct aub_decode_urb_stage_state *stages) 658{ 659 struct batch_window *window = (struct batch_window *) user_data; 660 struct urb_window *urb_window = new_urb_window(&window->decode_ctx, 0); 661 662 list_add(&urb_window->base.parent_link, &window->base.children_windows); 663} 664 665static void 666batch_edit_address(void *user_data, uint64_t address, uint32_t len) 667{ 668 struct batch_window *window = (struct batch_window *) user_data; 669 struct edit_window *edit_window = 670 new_edit_window(&window->mem, address, len); 671 672 list_add(&edit_window->base.parent_link, &window->base.children_windows); 673} 674 675static struct gen_batch_decode_bo 676batch_get_bo(void *user_data, bool ppgtt, uint64_t address) 677{ 678 struct batch_window *window = (struct batch_window *) user_data; 679 680 if (window->uses_ppgtt && ppgtt) 681 return aub_mem_get_ppgtt_bo(&window->mem, address); 682 else 683 return aub_mem_get_ggtt_bo(&window->mem, address); 684} 685 686static void 687update_batch_window(struct batch_window *window, bool reset, int exec_idx) 688{ 689 if (reset) 690 aub_mem_fini(&window->mem); 691 aub_mem_init(&window->mem); 692 693 window->exec_idx = MAX2(MIN2(context.file->n_execs - 1, exec_idx), 0); 694 update_mem_for_exec(&window->mem, context.file, window->exec_idx); 695} 696 697static void 698display_batch_ring_write(void *user_data, enum drm_i915_gem_engine_class engine, 699 const void *data, uint32_t data_len) 700{ 701 struct batch_window *window = (struct batch_window *) user_data; 702 703 window->uses_ppgtt = false; 704 705 aub_viewer_render_batch(&window->decode_ctx, data, data_len, 0, false); 706} 707 708static void 709display_batch_execlist_write(void *user_data, 710 enum drm_i915_gem_engine_class engine, 711 uint64_t context_descriptor) 712{ 713 struct batch_window *window = (struct batch_window *) user_data; 714 715 const uint32_t pphwsp_size = 4096; 716 uint32_t pphwsp_addr = context_descriptor & 0xfffff000; 717 struct gen_batch_decode_bo pphwsp_bo = 718 aub_mem_get_ggtt_bo(&window->mem, pphwsp_addr); 719 uint32_t *context_img = (uint32_t *)((uint8_t *)pphwsp_bo.map + 720 (pphwsp_addr - pphwsp_bo.addr) + 721 pphwsp_size); 722 723 uint32_t ring_buffer_head = context_img[5]; 724 uint32_t ring_buffer_tail = context_img[7]; 725 uint32_t ring_buffer_start = context_img[9]; 726 uint32_t ring_buffer_length = (context_img[11] & 0x1ff000) + 4096; 727 728 window->mem.pml4 = (uint64_t)context_img[49] << 32 | context_img[51]; 729 730 struct gen_batch_decode_bo ring_bo = 731 aub_mem_get_ggtt_bo(&window->mem, ring_buffer_start); 732 assert(ring_bo.size > 0); 733 void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head; 734 735 window->uses_ppgtt = true; 736 737 window->decode_ctx.engine = engine; 738 aub_viewer_render_batch(&window->decode_ctx, commands, 739 MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length), 740 ring_buffer_start + ring_buffer_head, true); 741} 742 743static void 744display_batch_window(struct window *win) 745{ 746 struct batch_window *window = (struct batch_window *) win; 747 748 ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2)); 749 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere(); 750 display_decode_options(&window->decode_cfg); 751 ImGui::PopItemWidth(); 752 753 if (ImGui::InputInt("Execbuf", &window->exec_idx)) 754 update_batch_window(window, true, window->exec_idx); 755 756 if (window_has_ctrl_key('p')) 757 update_batch_window(window, true, window->exec_idx - 1); 758 if (window_has_ctrl_key('n')) 759 update_batch_window(window, true, window->exec_idx + 1); 760 761 ImGui::Text("execbuf %i", window->exec_idx); 762 if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window->pml4_window, &window->mem); } 763 764 ImGui::BeginChild(ImGui::GetID("##block")); 765 766 struct aub_read read = {}; 767 read.user_data = window; 768 read.ring_write = display_batch_ring_write; 769 read.execlist_write = display_batch_execlist_write; 770 771 const uint8_t *iter = context.file->execs[window->exec_idx].start; 772 while (iter < context.file->execs[window->exec_idx].end) { 773 iter += aub_read_command(&read, iter, 774 context.file->execs[window->exec_idx].end - iter); 775 } 776 777 ImGui::EndChild(); 778} 779 780static void 781destroy_batch_window(struct window *win) 782{ 783 struct batch_window *window = (struct batch_window *) win; 784 785 aub_mem_fini(&window->mem); 786 787 /* This works because children windows are inserted at the back of the 788 * list, ensuring the deletion loop goes through the children after calling 789 * this function. 790 */ 791 list_for_each_entry(struct window, child_window, 792 &window->base.children_windows, parent_link) 793 child_window->opened = false; 794 window->pml4_window.base.opened = false; 795 796 free(window); 797} 798 799static void 800new_batch_window(int exec_idx) 801{ 802 struct batch_window *window = xtzalloc(*window); 803 804 snprintf(window->base.name, sizeof(window->base.name), 805 "Batch view##%p", window); 806 807 list_inithead(&window->base.parent_link); 808 list_inithead(&window->base.children_windows); 809 window->base.position = ImVec2(-1, -1); 810 window->base.size = ImVec2(600, 700); 811 window->base.opened = true; 812 window->base.display = display_batch_window; 813 window->base.destroy = destroy_batch_window; 814 815 window->collapsed = true; 816 window->decode_cfg = aub_viewer_decode_cfg(); 817 818 aub_viewer_decode_ctx_init(&window->decode_ctx, 819 &context.cfg, 820 &window->decode_cfg, 821 context.file->spec, 822 context.file->disasm, 823 batch_get_bo, 824 NULL, 825 window); 826 window->decode_ctx.display_shader = batch_display_shader; 827 window->decode_ctx.display_urb = batch_display_urb; 828 window->decode_ctx.edit_address = batch_edit_address; 829 830 update_batch_window(window, false, exec_idx); 831 832 list_addtail(&window->base.link, &context.windows); 833} 834 835/**/ 836 837static void 838display_registers_window(struct window *win) 839{ 840 static struct ImGuiTextFilter filter; 841 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere(); 842 filter.Draw(); 843 844 ImGui::BeginChild(ImGui::GetID("##block")); 845 hash_table_foreach(context.file->spec->registers_by_name, entry) { 846 struct gen_group *reg = (struct gen_group *) entry->data; 847 if (filter.PassFilter(reg->name) && 848 ImGui::CollapsingHeader(reg->name)) { 849 const struct gen_field *field = reg->fields; 850 while (field) { 851 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end); 852 field = field->next; 853 } 854 } 855 } 856 ImGui::EndChild(); 857} 858 859static void 860show_register_window(void) 861{ 862 struct window *window = &context.registers_window; 863 864 if (window->opened) { 865 window->opened = false; 866 return; 867 } 868 869 snprintf(window->name, sizeof(window->name), "Registers"); 870 871 list_inithead(&window->parent_link); 872 window->position = ImVec2(-1, -1); 873 window->size = ImVec2(200, 400); 874 window->opened = true; 875 window->display = display_registers_window; 876 window->destroy = destroy_window_noop; 877 878 list_addtail(&window->link, &context.windows); 879} 880 881static void 882display_commands_window(struct window *win) 883{ 884 static struct ImGuiTextFilter cmd_filter; 885 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere(); 886 cmd_filter.Draw("name filter"); 887 static struct ImGuiTextFilter field_filter; 888 field_filter.Draw("field filter"); 889 890 static char opcode_str[9] = { 0, }; 891 ImGui::InputText("opcode filter", opcode_str, sizeof(opcode_str), 892 ImGuiInputTextFlags_CharsHexadecimal); 893 size_t opcode_len = strlen(opcode_str); 894 uint64_t opcode = strtol(opcode_str, NULL, 16); 895 896 static bool show_dwords = true; 897 if (ImGui::Button("Dwords")) show_dwords ^= 1; 898 899 ImGui::BeginChild(ImGui::GetID("##block")); 900 hash_table_foreach(context.file->spec->commands, entry) { 901 struct gen_group *cmd = (struct gen_group *) entry->data; 902 if ((cmd_filter.PassFilter(cmd->name) && 903 (opcode_len == 0 || (opcode & cmd->opcode_mask) == cmd->opcode)) && 904 ImGui::CollapsingHeader(cmd->name)) { 905 const struct gen_field *field = cmd->fields; 906 int32_t last_dword = -1; 907 while (field) { 908 if (show_dwords && field->start / 32 != last_dword) { 909 for (last_dword = MAX2(0, last_dword + 1); 910 last_dword < field->start / 32; last_dword++) { 911 ImGui::TextColored(context.cfg.dwords_color, 912 "Dword %d", last_dword); 913 } 914 ImGui::TextColored(context.cfg.dwords_color, "Dword %d", last_dword); 915 } 916 if (field_filter.PassFilter(field->name)) 917 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end); 918 field = field->next; 919 } 920 } 921 } 922 hash_table_foreach(context.file->spec->structs, entry) { 923 struct gen_group *cmd = (struct gen_group *) entry->data; 924 if (cmd_filter.PassFilter(cmd->name) && opcode_len == 0 && 925 ImGui::CollapsingHeader(cmd->name)) { 926 const struct gen_field *field = cmd->fields; 927 int32_t last_dword = -1; 928 while (field) { 929 if (show_dwords && field->start / 32 != last_dword) { 930 last_dword = field->start / 32; 931 ImGui::TextColored(context.cfg.dwords_color, 932 "Dword %d", last_dword); 933 } 934 if (field_filter.PassFilter(field->name)) 935 ImGui::Text("%s : %i -> %i\n", field->name, field->start, field->end); 936 field = field->next; 937 } 938 } 939 } 940 ImGui::EndChild(); 941} 942 943static void 944show_commands_window(void) 945{ 946 struct window *window = &context.commands_window; 947 948 if (window->opened) { 949 window->opened = false; 950 return; 951 } 952 953 snprintf(window->name, sizeof(window->name), "Commands & structs"); 954 955 list_inithead(&window->parent_link); 956 window->position = ImVec2(-1, -1); 957 window->size = ImVec2(300, 400); 958 window->opened = true; 959 window->display = display_commands_window; 960 window->destroy = destroy_window_noop; 961 962 list_addtail(&window->link, &context.windows); 963} 964 965/* Main window */ 966 967static const char * 968human_size(size_t size) 969{ 970 unsigned divisions = 0; 971 double v = size; 972 double divider = 1024; 973 while (v >= divider) { 974 v /= divider; 975 divisions++; 976 } 977 978 static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" }; 979 static char result[20]; 980 snprintf(result, sizeof(result), "%.2f %s", 981 v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]); 982 return result; 983} 984 985static void 986display_aubfile_window(struct window *win) 987{ 988 ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha | 989 ImGuiColorEditFlags_NoLabel | 990 ImGuiColorEditFlags_NoInputs); 991 struct aub_viewer_cfg *cfg = &context.cfg; 992 993 ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine(); 994 ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine(); 995 ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine(); 996 ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine(); 997 ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine(); 998 ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine(); 999 1000 if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine(); 1001 if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine(); 1002 if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); } 1003 1004 if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); } 1005 1006 ImGui::Text("File name: %s", context.input_file); 1007 ImGui::Text("File size: %s", human_size(context.file->end - context.file->map)); 1008 ImGui::Text("Execbufs %u", context.file->n_execs); 1009 ImGui::Text("PCI ID: 0x%x", context.file->pci_id); 1010 ImGui::Text("Application name: %s", context.file->app_name); 1011 ImGui::Text("%s", gen_get_device_name(context.file->pci_id)); 1012 1013 ImGui::SetNextWindowContentWidth(500); 1014 if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { 1015 ImGui::Text("Some global keybindings:"); 1016 ImGui::Separator(); 1017 1018 static const char *texts[] = { 1019 "Ctrl-h", "show this screen", 1020 "Ctrl-c", "show commands list", 1021 "Ctrl-r", "show registers list", 1022 "Ctrl-b", "new batch window", 1023 "Ctrl-p/n", "switch to previous/next batch buffer", 1024 "Ctrl-Tab", "switch focus between window", 1025 "Ctrl-left/right", "align window to the side of the screen", 1026 }; 1027 float align = 0.0f; 1028 for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) 1029 align = MAX2(align, ImGui::CalcTextSize(texts[i]).x); 1030 align += ImGui::GetStyle().WindowPadding.x + 10; 1031 1032 for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) { 1033 ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]); 1034 } 1035 1036 if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape)) 1037 ImGui::CloseCurrentPopup(); 1038 ImGui::EndPopup(); 1039 } 1040} 1041 1042static void 1043show_aubfile_window(void) 1044{ 1045 struct window *window = &context.file_window; 1046 1047 if (window->opened) 1048 return; 1049 1050 snprintf(window->name, sizeof(window->name), 1051 "Aubinator Viewer: Intel AUB file decoder/editor"); 1052 1053 list_inithead(&window->parent_link); 1054 window->size = ImVec2(-1, 250); 1055 window->position = ImVec2(0, 0); 1056 window->opened = true; 1057 window->display = display_aubfile_window; 1058 window->destroy = NULL; 1059 1060 list_addtail(&window->link, &context.windows); 1061} 1062 1063/* Main redrawing */ 1064 1065static void 1066display_windows(void) 1067{ 1068 /* Start by disposing closed windows, we don't want to destroy windows that 1069 * have already been scheduled to be painted. So destroy always happens on 1070 * the next draw cycle, prior to any drawing. 1071 */ 1072 list_for_each_entry_safe(struct window, window, &context.windows, link) { 1073 if (window->opened) 1074 continue; 1075 1076 /* Can't close this one. */ 1077 if (window == &context.file_window) { 1078 window->opened = true; 1079 continue; 1080 } 1081 1082 list_del(&window->link); 1083 list_del(&window->parent_link); 1084 if (window->destroy) 1085 window->destroy(window); 1086 } 1087 1088 list_for_each_entry(struct window, window, &context.windows, link) { 1089 ImGui::SetNextWindowPos(window->position, ImGuiCond_FirstUseEver); 1090 ImGui::SetNextWindowSize(window->size, ImGuiCond_FirstUseEver); 1091 if (ImGui::Begin(window->name, &window->opened)) { 1092 window->display(window); 1093 window->position = ImGui::GetWindowPos(); 1094 window->size = ImGui::GetWindowSize(); 1095 } 1096 if (window_has_ctrl_key('w')) 1097 window->opened = false; 1098 ImGui::End(); 1099 } 1100} 1101 1102static void 1103repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context) 1104{ 1105 ImGui_ImplOpenGL3_NewFrame(); 1106 ImGui_ImplGtk3_NewFrame(); 1107 ImGui::NewFrame(); 1108 1109 display_windows(); 1110 1111 ImGui::EndFrame(); 1112 ImGui::Render(); 1113 1114 glClearColor(context.cfg.clear_color.Value.x, 1115 context.cfg.clear_color.Value.y, 1116 context.cfg.clear_color.Value.z, 1.0); 1117 glClear(GL_COLOR_BUFFER_BIT); 1118 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 1119} 1120 1121static void 1122realize_area(GtkGLArea *area) 1123{ 1124 ImGui::CreateContext(); 1125 ImGui_ImplGtk3_Init(GTK_WIDGET(area), true); 1126 ImGui_ImplOpenGL3_Init("#version 130"); 1127 1128 list_inithead(&context.windows); 1129 1130 ImGui::StyleColorsDark(); 1131 context.cfg = aub_viewer_cfg(); 1132 1133 ImGuiIO& io = ImGui::GetIO(); 1134 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; 1135} 1136 1137static void 1138unrealize_area(GtkGLArea *area) 1139{ 1140 gtk_gl_area_make_current(area); 1141 1142 ImGui_ImplOpenGL3_Shutdown(); 1143 ImGui_ImplGtk3_Shutdown(); 1144 ImGui::DestroyContext(); 1145} 1146 1147static void 1148size_allocate_area(GtkGLArea *area, 1149 GdkRectangle *allocation, 1150 gpointer user_data) 1151{ 1152 if (!gtk_widget_get_realized(GTK_WIDGET(area))) 1153 return; 1154 1155 /* We want to catch only initial size allocate. */ 1156 g_signal_handlers_disconnect_by_func(area, 1157 (gpointer) size_allocate_area, 1158 user_data); 1159 show_aubfile_window(); 1160} 1161 1162static void 1163print_help(const char *progname, FILE *file) 1164{ 1165 fprintf(file, 1166 "Usage: %s [OPTION]... FILE\n" 1167 "Decode aub file contents from FILE.\n\n" 1168 " --help display this help and exit\n" 1169 " -x, --xml=DIR load hardware xml description from directory DIR\n", 1170 progname); 1171} 1172 1173int main(int argc, char *argv[]) 1174{ 1175 int c, i; 1176 bool help = false; 1177 const struct option aubinator_opts[] = { 1178 { "help", no_argument, (int *) &help, true }, 1179 { "xml", required_argument, NULL, 'x' }, 1180 { NULL, 0, NULL, 0 } 1181 }; 1182 1183 memset(&context, 0, sizeof(context)); 1184 1185 i = 0; 1186 while ((c = getopt_long(argc, argv, "x:s:", aubinator_opts, &i)) != -1) { 1187 switch (c) { 1188 case 'x': 1189 context.xml_path = strdup(optarg); 1190 break; 1191 default: 1192 break; 1193 } 1194 } 1195 1196 if (optind < argc) 1197 context.input_file = argv[optind]; 1198 1199 if (help || !context.input_file) { 1200 print_help(argv[0], stderr); 1201 exit(0); 1202 } 1203 1204 context.file = aub_file_open(context.input_file); 1205 1206 gtk_init(NULL, NULL); 1207 1208 context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 1209 gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Aubinator Viewer"); 1210 g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL); 1211 gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720); 1212 1213 GtkWidget* gl_area = gtk_gl_area_new(); 1214 g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL); 1215 g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL); 1216 g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL); 1217 g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL); 1218 gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area); 1219 1220 gtk_widget_show_all(context.gtk_window); 1221 1222 gtk_main(); 1223 1224 free(context.xml_path); 1225 1226 return EXIT_SUCCESS; 1227} 1228