2 * Copyright © 2016 Intel Corporation
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:
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
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
34 #include <sys/types.h>
37 #include "util/macros.h"
42 #include "common/gen_disasm.h"
44 #define xtzalloc(name) ((decltype(&name)) calloc(1, sizeof(name)))
45 #define xtalloc(name) ((decltype(&name)) malloc(sizeof(name)))
48 uint8_t *map
, *end
, *cursor
;
53 /* List of batch buffers to process */
59 int n_allocated_execs
;
61 uint32_t idx_reg_write
;
64 struct gen_device_info devinfo
;
65 struct gen_spec
*spec
;
69 store_exec_begin(struct aub_file
*file
)
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]));
79 file
->execs
[file
->n_execs
++].start
= file
->cursor
;
83 store_exec_end(struct aub_file
*file
)
85 if (file
->n_execs
> 0 && file
->execs
[file
->n_execs
- 1].end
== NULL
)
86 file
->execs
[file
->n_execs
- 1].end
= file
->cursor
;
90 handle_mem_write(void *user_data
, uint64_t phys_addr
,
91 const void *data
, uint32_t data_len
)
93 struct aub_file
*file
= (struct aub_file
*) user_data
;
94 file
->idx_reg_write
= 0;
99 handle_ring_write(void *user_data
, enum drm_i915_gem_engine_class engine
,
100 const void *ring_data
, uint32_t ring_data_len
)
102 struct aub_file
*file
= (struct aub_file
*) user_data
;
103 file
->idx_reg_write
= 0;
104 store_exec_begin(file
);
108 handle_reg_write(void *user_data
, uint32_t reg_offset
, uint32_t reg_value
)
110 struct aub_file
*file
= (struct aub_file
*) user_data
;
112 /* Only store the first register write of a series (execlist writes take
115 if (file
->idx_reg_write
++ == 0)
116 store_exec_begin(file
);
120 handle_info(void *user_data
, int pci_id
, const char *app_name
)
122 struct aub_file
*file
= (struct aub_file
*) user_data
;
123 store_exec_end(file
);
125 file
->pci_id
= pci_id
;
126 snprintf(file
->app_name
, sizeof(app_name
), "%s", app_name
);
128 if (!gen_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
);
132 file
->spec
= gen_spec_load(&file
->devinfo
);
136 handle_error(void *user_data
, const void *aub_data
, const char *msg
)
138 fprintf(stderr
, "ERROR: %s\n", msg
);
141 static struct aub_file
*
142 aub_file_open(const char *filename
)
144 struct aub_file
*file
;
148 file
= xtzalloc(*file
);
149 fd
= open(filename
, O_RDWR
);
151 fprintf(stderr
, "open %s failed: %s\n", filename
, strerror(errno
));
155 if (fstat(fd
, &sb
) == -1) {
156 fprintf(stderr
, "stat failed: %s\n", strerror(errno
));
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
));
169 file
->cursor
= file
->map
;
170 file
->end
= file
->map
+ sb
.st_size
;
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
;
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
;
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
;
200 update_mem_for_exec(struct aub_mem
*mem
, struct aub_file
*file
, int exec_idx
)
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
;
209 /* Replay the aub file from the beginning up to just before the
210 * commands we want to read. where the context setup happens.
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
);
220 #include <epoxy/gl.h>
222 #include "imgui/imgui.h"
223 #include "imgui/imgui_memory_editor.h"
224 #include "imgui_impl_gtk3.h"
225 #include "imgui_impl_opengl3.h"
227 #include "aubinator_viewer.h"
228 #include "aubinator_viewer_urb.h"
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 */
234 struct list_head children_windows
; /* list of children windows */
242 void (*display
)(struct window
*);
243 void (*destroy
)(struct window
*);
253 struct gen_batch_decode_bo aub_bo
;
256 struct gen_batch_decode_bo gtt_bo
;
259 struct MemoryEditor editor
;
268 struct shader_window
{
279 uint32_t end_urb_offset
;
280 struct aub_decode_urb_stage_state urb_stages
[AUB_DECODE_N_STAGE
];
282 AubinatorViewerUrb urb_view
;
285 struct batch_window
{
289 struct aub_read read
;
296 struct aub_viewer_decode_cfg decode_cfg
;
297 struct aub_viewer_decode_ctx decode_ctx
;
299 struct pml4_window pml4_window
;
301 char edit_address
[20];
304 static struct Context
{
305 struct aub_file
*file
;
309 GtkWidget
*gtk_window
;
312 bool show_commands_window
;
313 bool show_registers_window
;
315 struct aub_viewer_cfg cfg
;
317 struct list_head windows
;
319 struct window file_window
;
320 struct window commands_window
;
321 struct window registers_window
;
324 thread_local ImGuiContext
* __MesaImGui
;
329 return ImGuiKey_COUNT
+ k
;
333 has_ctrl_key(int key
)
335 return ImGui::GetIO().KeyCtrl
&& ImGui::IsKeyPressed(map_key(key
));
339 window_has_ctrl_key(int key
)
341 return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key
);
345 destroy_window_noop(struct window
*win
)
352 display_shader_window(struct window
*win
)
354 struct shader_window
*window
= (struct shader_window
*) win
;
356 if (window
->shader
) {
357 ImGui::InputTextMultiline("Assembly",
358 window
->shader
, window
->shader_size
,
359 ImGui::GetContentRegionAvail(),
360 ImGuiInputTextFlags_ReadOnly
);
362 ImGui::Text("Shader not available");
367 destroy_shader_window(struct window
*win
)
369 struct shader_window
*window
= (struct shader_window
*) win
;
371 free(window
->shader
);
375 static struct shader_window
*
376 new_shader_window(struct aub_mem
*mem
, uint64_t address
, const char *desc
)
378 struct shader_window
*window
= xtzalloc(*window
);
380 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
381 "%s (0x%" PRIx64
")##%p", desc
, address
, window
);
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
;
390 struct gen_batch_decode_bo shader_bo
=
391 aub_mem_get_ppgtt_bo(mem
, address
);
393 FILE *f
= open_memstream(&window
->shader
, &window
->shader_size
);
395 gen_disassemble(&context
.file
->devinfo
,
396 (const uint8_t *) shader_bo
.map
+
397 (address
- shader_bo
.addr
), 0, f
);
402 list_addtail(&window
->base
.link
, &context
.windows
);
410 display_urb_window(struct window
*win
)
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",
422 ImGui::Text("URB allocation:");
423 window
->urb_view
.DrawAllocation("##urb",
424 ARRAY_SIZE(window
->urb_stages
),
425 window
->end_urb_offset
,
427 &window
->urb_stages
[0]);
431 destroy_urb_window(struct window
*win
)
433 struct urb_window
*window
= (struct urb_window
*) win
;
438 static struct urb_window
*
439 new_urb_window(struct aub_viewer_decode_ctx
*decode_ctx
, uint64_t address
)
441 struct urb_window
*window
= xtzalloc(*window
);
443 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
444 "URB view (0x%" PRIx64
")##%p", address
, window
);
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
;
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();
457 list_addtail(&window
->base
.link
, &context
.windows
);
462 /* Memory editor windows */
465 read_edit_window(const uint8_t *data
, size_t off
)
467 struct edit_window
*window
= (struct edit_window
*) data
;
469 return *((const uint8_t *) window
->gtt_bo
.map
+ window
->gtt_offset
+ off
);
473 write_edit_window(uint8_t *data
, size_t off
, uint8_t d
)
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
;
483 display_edit_window(struct window
*win
)
485 struct edit_window
*window
= (struct edit_window
*) win
;
487 if (window
->aub_bo
.map
&& window
->gtt_bo
.map
) {
488 ImGui::BeginChild(ImGui::GetID("##block"));
489 window
->editor
.DrawContents((uint8_t *) window
,
491 window
->gtt_bo
.size
- window
->gtt_offset
,
492 window
->aub_bo
.size
- window
->aub_offset
),
496 ImGui::Text("Memory view at 0x%" PRIx64
" not available", window
->address
);
501 destroy_edit_window(struct window
*win
)
503 struct edit_window
*window
= (struct edit_window
*) win
;
505 if (window
->aub_bo
.map
)
506 mprotect((void *) window
->aub_bo
.map
, 4096, PROT_READ
);
510 static struct edit_window
*
511 new_edit_window(struct aub_mem
*mem
, uint64_t address
, uint32_t len
)
513 struct edit_window
*window
= xtzalloc(*window
);
515 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
516 "Editing aub at 0x%" PRIx64
"##%p", address
, window
);
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
;
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
);
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
;
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
;
541 if (mprotect((void *) window
->aub_bo
.map
, window
->aub_bo
.size
, PROT_READ
| PROT_WRITE
) != 0) {
542 window
->aub_bo
.map
= NULL
;
546 window
->gtt_offset
= address
- window
->gtt_bo
.addr
;
548 list_addtail(&window
->base
.link
, &context
.windows
);
553 /* 4 level page table walk windows */
556 display_pml4_level(struct aub_mem
*mem
, uint64_t table_addr
, uint64_t table_virt_addr
, int level
)
561 struct gen_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
);
566 ImGui::TextColored(context
.cfg
.missing_color
, "Page not available");
570 uint64_t addr_increment
= 1ULL << (12 + 9 * (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
;
578 ImGui::Text("Entry%03i - phys_addr=0x%" PRIx64
" - virt_addr=0x%" PRIx64
,
579 e
, table
[e
], entry_virt_addr
);
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
;
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);
598 display_pml4_window(struct window
*win
)
600 struct pml4_window
*window
= (struct pml4_window
*) win
;
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);
609 show_pml4_window(struct pml4_window
*window
, struct aub_mem
*mem
)
611 if (window
->base
.opened
) {
612 window
->base
.opened
= false;
616 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
617 "4-Level page tables##%p", window
);
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
;
628 list_addtail(&window
->base
.link
, &context
.windows
);
631 /* Batch decoding windows */
634 display_decode_options(struct aub_viewer_decode_cfg
*cfg
)
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;
645 batch_display_shader(void *user_data
, const char *shader_desc
, uint64_t address
)
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
);
651 list_add(&shader_window
->base
.parent_link
, &window
->base
.children_windows
);
655 batch_display_urb(void *user_data
, const struct aub_decode_urb_stage_state
*stages
)
657 struct batch_window
*window
= (struct batch_window
*) user_data
;
658 struct urb_window
*urb_window
= new_urb_window(&window
->decode_ctx
, 0);
660 list_add(&urb_window
->base
.parent_link
, &window
->base
.children_windows
);
664 batch_edit_address(void *user_data
, uint64_t address
, uint32_t len
)
666 struct batch_window
*window
= (struct batch_window
*) user_data
;
667 struct edit_window
*edit_window
=
668 new_edit_window(&window
->mem
, address
, len
);
670 list_add(&edit_window
->base
.parent_link
, &window
->base
.children_windows
);
673 static struct gen_batch_decode_bo
674 batch_get_bo(void *user_data
, bool ppgtt
, uint64_t address
)
676 struct batch_window
*window
= (struct batch_window
*) user_data
;
678 if (window
->uses_ppgtt
&& ppgtt
)
679 return aub_mem_get_ppgtt_bo(&window
->mem
, address
);
681 return aub_mem_get_ggtt_bo(&window
->mem
, address
);
685 update_batch_window(struct batch_window
*window
, bool reset
, int exec_idx
)
688 aub_mem_fini(&window
->mem
);
689 aub_mem_init(&window
->mem
);
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
);
696 display_batch_ring_write(void *user_data
, enum drm_i915_gem_engine_class engine
,
697 const void *data
, uint32_t data_len
)
699 struct batch_window
*window
= (struct batch_window
*) user_data
;
701 window
->uses_ppgtt
= false;
703 aub_viewer_render_batch(&window
->decode_ctx
, data
, data_len
, 0, false);
707 display_batch_execlist_write(void *user_data
,
708 enum drm_i915_gem_engine_class engine
,
709 uint64_t context_descriptor
)
711 struct batch_window
*window
= (struct batch_window
*) user_data
;
713 const uint32_t pphwsp_size
= 4096;
714 uint32_t pphwsp_addr
= context_descriptor
& 0xfffff000;
715 struct gen_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
) +
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;
726 window
->mem
.pml4
= (uint64_t)context_img
[49] << 32 | context_img
[51];
728 struct gen_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
;
733 window
->uses_ppgtt
= true;
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);
742 display_batch_window(struct window
*win
)
744 struct batch_window
*window
= (struct batch_window
*) win
;
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();
751 if (ImGui::InputInt("Execbuf", &window
->exec_idx
))
752 update_batch_window(window
, true, window
->exec_idx
);
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);
759 ImGui::Text("execbuf %i", window
->exec_idx
);
760 if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window
->pml4_window
, &window
->mem
); }
762 ImGui::BeginChild(ImGui::GetID("##block"));
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
;
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
);
779 destroy_batch_window(struct window
*win
)
781 struct batch_window
*window
= (struct batch_window
*) win
;
783 aub_mem_fini(&window
->mem
);
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
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;
798 new_batch_window(int exec_idx
)
800 struct batch_window
*window
= xtzalloc(*window
);
802 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
803 "Batch view##%p", window
);
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
;
813 window
->collapsed
= true;
814 window
->decode_cfg
= aub_viewer_decode_cfg();
816 aub_viewer_decode_ctx_init(&window
->decode_ctx
,
819 &context
.file
->devinfo
,
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
;
828 update_batch_window(window
, false, exec_idx
);
830 list_addtail(&window
->base
.link
, &context
.windows
);
836 display_registers_window(struct window
*win
)
838 static struct ImGuiTextFilter filter
;
839 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
842 ImGui::BeginChild(ImGui::GetID("##block"));
843 hash_table_foreach(context
.file
->spec
->registers_by_name
, entry
) {
844 struct gen_group
*reg
= (struct gen_group
*) entry
->data
;
845 if (filter
.PassFilter(reg
->name
) &&
846 ImGui::CollapsingHeader(reg
->name
)) {
847 const struct gen_field
*field
= reg
->fields
;
849 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
858 show_register_window(void)
860 struct window
*window
= &context
.registers_window
;
862 if (window
->opened
) {
863 window
->opened
= false;
867 snprintf(window
->name
, sizeof(window
->name
), "Registers");
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
;
876 list_addtail(&window
->link
, &context
.windows
);
880 display_commands_window(struct window
*win
)
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");
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);
894 static bool show_dwords
= true;
895 if (ImGui::Button("Dwords")) show_dwords
^= 1;
897 ImGui::BeginChild(ImGui::GetID("##block"));
898 hash_table_foreach(context
.file
->spec
->commands
, entry
) {
899 struct gen_group
*cmd
= (struct gen_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 gen_field
*field
= cmd
->fields
;
904 int32_t last_dword
= -1;
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
);
912 ImGui::TextColored(context
.cfg
.dwords_color
, "Dword %d", last_dword
);
914 if (field_filter
.PassFilter(field
->name
))
915 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
920 hash_table_foreach(context
.file
->spec
->structs
, entry
) {
921 struct gen_group
*cmd
= (struct gen_group
*) entry
->data
;
922 if (cmd_filter
.PassFilter(cmd
->name
) && opcode_len
== 0 &&
923 ImGui::CollapsingHeader(cmd
->name
)) {
924 const struct gen_field
*field
= cmd
->fields
;
925 int32_t last_dword
= -1;
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
);
932 if (field_filter
.PassFilter(field
->name
))
933 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
942 show_commands_window(void)
944 struct window
*window
= &context
.commands_window
;
946 if (window
->opened
) {
947 window
->opened
= false;
951 snprintf(window
->name
, sizeof(window
->name
), "Commands & structs");
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
;
960 list_addtail(&window
->link
, &context
.windows
);
966 human_size(size_t size
)
968 unsigned divisions
= 0;
970 double divider
= 1024;
971 while (v
>= divider
) {
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
]);
984 display_aubfile_window(struct window
*win
)
986 ImGuiColorEditFlags cflags
= (ImGuiColorEditFlags_NoAlpha
|
987 ImGuiColorEditFlags_NoLabel
|
988 ImGuiColorEditFlags_NoInputs
);
989 struct aub_viewer_cfg
*cfg
= &context
.cfg
;
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();
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"); }
1002 if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
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", gen_get_device_name(context
.file
->pci_id
));
1011 ImGui::SetNextWindowContentWidth(500);
1012 if (ImGui::BeginPopupModal("Help", NULL
, ImGuiWindowFlags_AlwaysAutoResize
)) {
1013 ImGui::Text("Some global keybindings:");
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",
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;
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]);
1034 if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape
))
1035 ImGui::CloseCurrentPopup();
1041 show_aubfile_window(void)
1043 struct window
*window
= &context
.file_window
;
1048 snprintf(window
->name
, sizeof(window
->name
),
1049 "Aubinator Viewer: Intel AUB file decoder/editor");
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
;
1058 list_addtail(&window
->link
, &context
.windows
);
1061 /* Main redrawing */
1064 display_windows(void)
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.
1070 list_for_each_entry_safe(struct window
, window
, &context
.windows
, link
) {
1074 /* Can't close this one. */
1075 if (window
== &context
.file_window
) {
1076 window
->opened
= true;
1080 list_del(&window
->link
);
1081 list_del(&window
->parent_link
);
1082 if (window
->destroy
)
1083 window
->destroy(window
);
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();
1094 if (window_has_ctrl_key('w'))
1095 window
->opened
= false;
1101 repaint_area(GtkGLArea
*area
, GdkGLContext
*gdk_gl_context
)
1103 ImGui_ImplOpenGL3_NewFrame();
1104 ImGui_ImplGtk3_NewFrame();
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());
1120 realize_area(GtkGLArea
*area
)
1122 ImGui::CreateContext();
1123 ImGui_ImplGtk3_Init(GTK_WIDGET(area
), true);
1124 ImGui_ImplOpenGL3_Init("#version 130");
1126 list_inithead(&context
.windows
);
1128 ImGui::StyleColorsDark();
1129 context
.cfg
= aub_viewer_cfg();
1131 ImGuiIO
& io
= ImGui::GetIO();
1132 io
.ConfigFlags
|= ImGuiConfigFlags_NavEnableKeyboard
;
1136 unrealize_area(GtkGLArea
*area
)
1138 gtk_gl_area_make_current(area
);
1140 ImGui_ImplOpenGL3_Shutdown();
1141 ImGui_ImplGtk3_Shutdown();
1142 ImGui::DestroyContext();
1146 size_allocate_area(GtkGLArea
*area
,
1147 GdkRectangle
*allocation
,
1150 if (!gtk_widget_get_realized(GTK_WIDGET(area
)))
1153 /* We want to catch only initial size allocate. */
1154 g_signal_handlers_disconnect_by_func(area
,
1155 (gpointer
) size_allocate_area
,
1157 show_aubfile_window();
1161 print_help(const char *progname
, FILE *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",
1171 int main(int argc
, char *argv
[])
1175 const struct option aubinator_opts
[] = {
1176 { "help", no_argument
, (int *) &help
, true },
1177 { "xml", required_argument
, NULL
, 'x' },
1178 { NULL
, 0, NULL
, 0 }
1181 memset(&context
, 0, sizeof(context
));
1184 while ((c
= getopt_long(argc
, argv
, "x:s:", aubinator_opts
, &i
)) != -1) {
1187 context
.xml_path
= strdup(optarg
);
1195 context
.input_file
= argv
[optind
];
1197 if (help
|| !context
.input_file
) {
1198 print_help(argv
[0], stderr
);
1202 context
.file
= aub_file_open(context
.input_file
);
1204 gtk_init(NULL
, NULL
);
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);
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
);
1218 gtk_widget_show_all(context
.gtk_window
);
1222 free(context
.xml_path
);
1224 return EXIT_SUCCESS
;