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