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
;
66 struct gen_disasm
*disasm
;
70 store_exec_begin(struct aub_file
*file
)
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]));
80 file
->execs
[file
->n_execs
++].start
= file
->cursor
;
84 store_exec_end(struct aub_file
*file
)
86 if (file
->n_execs
> 0 && file
->execs
[file
->n_execs
- 1].end
== NULL
)
87 file
->execs
[file
->n_execs
- 1].end
= file
->cursor
;
91 handle_mem_write(void *user_data
, uint64_t phys_addr
,
92 const void *data
, uint32_t data_len
)
94 struct aub_file
*file
= (struct aub_file
*) user_data
;
95 file
->idx_reg_write
= 0;
100 handle_ring_write(void *user_data
, enum drm_i915_gem_engine_class engine
,
101 const void *ring_data
, uint32_t ring_data_len
)
103 struct aub_file
*file
= (struct aub_file
*) user_data
;
104 file
->idx_reg_write
= 0;
105 store_exec_begin(file
);
109 handle_reg_write(void *user_data
, uint32_t reg_offset
, uint32_t reg_value
)
111 struct aub_file
*file
= (struct aub_file
*) user_data
;
113 /* Only store the first register write of a series (execlist writes take
116 if (file
->idx_reg_write
++ == 0)
117 store_exec_begin(file
);
121 handle_info(void *user_data
, int pci_id
, const char *app_name
)
123 struct aub_file
*file
= (struct aub_file
*) user_data
;
124 store_exec_end(file
);
126 file
->pci_id
= pci_id
;
127 snprintf(file
->app_name
, sizeof(app_name
), "%s", app_name
);
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
);
133 file
->spec
= gen_spec_load(&file
->devinfo
);
134 file
->disasm
= gen_disasm_create(&file
->devinfo
);
138 handle_error(void *user_data
, const void *aub_data
, const char *msg
)
140 fprintf(stderr
, "ERROR: %s\n", msg
);
143 static struct aub_file
*
144 aub_file_open(const char *filename
)
146 struct aub_file
*file
;
150 file
= xtzalloc(*file
);
151 fd
= open(filename
, O_RDWR
);
153 fprintf(stderr
, "open %s failed: %s\n", filename
, strerror(errno
));
157 if (fstat(fd
, &sb
) == -1) {
158 fprintf(stderr
, "stat failed: %s\n", strerror(errno
));
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
));
171 file
->cursor
= file
->map
;
172 file
->end
= file
->map
+ sb
.st_size
;
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
;
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
;
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
;
202 update_mem_for_exec(struct aub_mem
*mem
, struct aub_file
*file
, int exec_idx
)
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
;
211 /* Replay the aub file from the beginning up to just before the
212 * commands we want to read. where the context setup happens.
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
);
222 #include <epoxy/gl.h>
225 #include "imgui_impl_gtk3.h"
226 #include "imgui_impl_opengl3.h"
228 #include "aubinator_viewer.h"
229 #include "aubinator_viewer_urb.h"
230 #include "imgui_memory_editor.h"
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 */
236 struct list_head children_windows
; /* list of children windows */
244 void (*display
)(struct window
*);
245 void (*destroy
)(struct window
*);
255 struct gen_batch_decode_bo aub_bo
;
258 struct gen_batch_decode_bo gtt_bo
;
261 struct MemoryEditor editor
;
270 struct shader_window
{
281 uint32_t end_urb_offset
;
282 struct aub_decode_urb_stage_state urb_stages
[AUB_DECODE_N_STAGE
];
284 AubinatorViewerUrb urb_view
;
287 struct batch_window
{
291 struct aub_read read
;
298 struct aub_viewer_decode_cfg decode_cfg
;
299 struct aub_viewer_decode_ctx decode_ctx
;
301 struct pml4_window pml4_window
;
303 char edit_address
[20];
306 static struct Context
{
307 struct aub_file
*file
;
311 GtkWidget
*gtk_window
;
314 bool show_commands_window
;
315 bool show_registers_window
;
317 struct aub_viewer_cfg cfg
;
319 struct list_head windows
;
321 struct window file_window
;
322 struct window commands_window
;
323 struct window registers_window
;
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%lx)##%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
;
392 shader_bo
= aub_mem_get_ppgtt_bo(mem
, address
);
394 shader_bo
= aub_mem_get_ggtt_bo(mem
, address
);
397 FILE *f
= open_memstream(&window
->shader
, &window
->shader_size
);
399 gen_disasm_disassemble(context
.file
->disasm
, shader_bo
.map
, 0, f
);
404 list_addtail(&window
->base
.link
, &context
.windows
);
412 display_urb_window(struct window
*win
)
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",
424 ImGui::Text("URB allocation:");
425 window
->urb_view
.DrawAllocation("##urb",
426 ARRAY_SIZE(window
->urb_stages
),
427 window
->end_urb_offset
,
429 &window
->urb_stages
[0]);
433 destroy_urb_window(struct window
*win
)
435 struct urb_window
*window
= (struct urb_window
*) win
;
440 static struct urb_window
*
441 new_urb_window(struct aub_viewer_decode_ctx
*decode_ctx
, uint64_t address
)
443 struct urb_window
*window
= xtzalloc(*window
);
445 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
446 "URB view (0x%lx)##%p", address
, window
);
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
;
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();
459 list_addtail(&window
->base
.link
, &context
.windows
);
464 /* Memory editor windows */
467 read_edit_window(const uint8_t *data
, size_t off
)
469 struct edit_window
*window
= (struct edit_window
*) data
;
471 return *((const uint8_t *) window
->gtt_bo
.map
+ window
->gtt_offset
+ off
);
475 write_edit_window(uint8_t *data
, size_t off
, uint8_t d
)
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
;
485 display_edit_window(struct window
*win
)
487 struct edit_window
*window
= (struct edit_window
*) win
;
489 if (window
->aub_bo
.map
&& window
->gtt_bo
.map
) {
490 ImGui::BeginChild(ImGui::GetID("##block"));
491 window
->editor
.DrawContents((uint8_t *) window
,
493 window
->gtt_bo
.size
- window
->gtt_offset
,
494 window
->aub_bo
.size
- window
->aub_offset
),
498 ImGui::Text("Memory view at 0x%lx not available", window
->address
);
503 destroy_edit_window(struct window
*win
)
505 struct edit_window
*window
= (struct edit_window
*) win
;
507 if (window
->aub_bo
.map
)
508 mprotect((void *) window
->aub_bo
.map
, 4096, PROT_READ
);
512 static struct edit_window
*
513 new_edit_window(struct aub_mem
*mem
, uint64_t address
, uint32_t len
)
515 struct edit_window
*window
= xtzalloc(*window
);
517 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
518 "Editing aub at 0x%lx##%p", address
, window
);
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
;
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
);
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
;
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
;
543 if (mprotect((void *) window
->aub_bo
.map
, window
->aub_bo
.size
, PROT_READ
| PROT_WRITE
) != 0) {
544 window
->aub_bo
.map
= NULL
;
548 window
->gtt_offset
= address
- window
->gtt_bo
.addr
;
550 list_addtail(&window
->base
.link
, &context
.windows
);
555 /* 4 level page table walk windows */
558 display_pml4_level(struct aub_mem
*mem
, uint64_t table_addr
, uint64_t table_virt_addr
, int level
)
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
);
568 ImGui::TextColored(context
.cfg
.missing_color
, "Page not available");
572 uint64_t addr_increment
= 1ULL << (12 + 9 * (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
;
580 ImGui::Text("Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx",
581 e
, table
[e
], entry_virt_addr
);
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
;
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);
600 display_pml4_window(struct window
*win
)
602 struct pml4_window
*window
= (struct pml4_window
*) win
;
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);
611 show_pml4_window(struct pml4_window
*window
, struct aub_mem
*mem
)
613 if (window
->base
.opened
) {
614 window
->base
.opened
= false;
618 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
619 "4-Level page tables##%p", window
);
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
;
630 list_addtail(&window
->base
.link
, &context
.windows
);
633 /* Batch decoding windows */
636 display_decode_options(struct aub_viewer_decode_cfg
*cfg
)
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;
647 batch_display_shader(void *user_data
, const char *shader_desc
, uint64_t address
)
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
);
653 list_add(&shader_window
->base
.parent_link
, &window
->base
.children_windows
);
657 batch_display_urb(void *user_data
, const struct aub_decode_urb_stage_state
*stages
)
659 struct batch_window
*window
= (struct batch_window
*) user_data
;
660 struct urb_window
*urb_window
= new_urb_window(&window
->decode_ctx
, 0);
662 list_add(&urb_window
->base
.parent_link
, &window
->base
.children_windows
);
666 batch_edit_address(void *user_data
, uint64_t address
, uint32_t len
)
668 struct batch_window
*window
= (struct batch_window
*) user_data
;
669 struct edit_window
*edit_window
=
670 new_edit_window(&window
->mem
, address
, len
);
672 list_add(&edit_window
->base
.parent_link
, &window
->base
.children_windows
);
675 static struct gen_batch_decode_bo
676 batch_get_bo(void *user_data
, uint64_t address
)
678 struct batch_window
*window
= (struct batch_window
*) user_data
;
680 if (window
->uses_ppgtt
)
681 return aub_mem_get_ppgtt_bo(&window
->mem
, address
);
683 return aub_mem_get_ggtt_bo(&window
->mem
, address
);
687 update_batch_window(struct batch_window
*window
, bool reset
, int exec_idx
)
690 aub_mem_fini(&window
->mem
);
691 aub_mem_init(&window
->mem
);
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
);
698 display_batch_ring_write(void *user_data
, enum drm_i915_gem_engine_class engine
,
699 const void *data
, uint32_t data_len
)
701 struct batch_window
*window
= (struct batch_window
*) user_data
;
703 window
->uses_ppgtt
= false;
705 aub_viewer_render_batch(&window
->decode_ctx
, data
, data_len
, 0);
709 display_batch_execlist_write(void *user_data
, enum drm_i915_gem_engine_class
,
710 uint64_t context_descriptor
)
712 struct batch_window
*window
= (struct batch_window
*) user_data
;
714 const uint32_t pphwsp_size
= 4096;
715 uint32_t pphwsp_addr
= context_descriptor
& 0xfffff000;
716 struct gen_batch_decode_bo pphwsp_bo
=
717 aub_mem_get_ggtt_bo(&window
->mem
, pphwsp_addr
);
718 uint32_t *context_img
= (uint32_t *)((uint8_t *)pphwsp_bo
.map
+
719 (pphwsp_addr
- pphwsp_bo
.addr
) +
722 uint32_t ring_buffer_head
= context_img
[5];
723 uint32_t ring_buffer_tail
= context_img
[7];
724 uint32_t ring_buffer_start
= context_img
[9];
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
);
733 window
->uses_ppgtt
= true;
735 aub_viewer_render_batch(&window
->decode_ctx
, commands
,
736 ring_buffer_tail
- ring_buffer_head
,
741 display_batch_window(struct window
*win
)
743 struct batch_window
*window
= (struct batch_window
*) win
;
745 ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
746 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
747 display_decode_options(&window
->decode_cfg
);
748 ImGui::PopItemWidth();
750 if (ImGui::InputInt("Execbuf", &window
->exec_idx
))
751 update_batch_window(window
, true, window
->exec_idx
);
753 if (window_has_ctrl_key('p'))
754 update_batch_window(window
, true, window
->exec_idx
- 1);
755 if (window_has_ctrl_key('n'))
756 update_batch_window(window
, true, window
->exec_idx
+ 1);
758 ImGui::Text("execbuf %i", window
->exec_idx
);
759 if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window
->pml4_window
, &window
->mem
); }
761 ImGui::BeginChild(ImGui::GetID("##block"));
763 struct aub_read read
= {};
764 read
.user_data
= window
;
765 read
.ring_write
= display_batch_ring_write
;
766 read
.execlist_write
= display_batch_execlist_write
;
768 const uint8_t *iter
= context
.file
->execs
[window
->exec_idx
].start
;
769 while (iter
< context
.file
->execs
[window
->exec_idx
].end
) {
770 iter
+= aub_read_command(&read
, iter
,
771 context
.file
->execs
[window
->exec_idx
].end
- iter
);
778 destroy_batch_window(struct window
*win
)
780 struct batch_window
*window
= (struct batch_window
*) win
;
782 aub_mem_fini(&window
->mem
);
784 /* This works because children windows are inserted at the back of the
785 * list, ensuring the deletion loop goes through the children after calling
788 list_for_each_entry(struct window
, child_window
,
789 &window
->base
.children_windows
, parent_link
)
790 child_window
->opened
= false;
791 window
->pml4_window
.base
.opened
= false;
797 new_batch_window(int exec_idx
)
799 struct batch_window
*window
= xtzalloc(*window
);
801 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
802 "Batch view##%p", window
);
804 list_inithead(&window
->base
.parent_link
);
805 list_inithead(&window
->base
.children_windows
);
806 window
->base
.position
= ImVec2(-1, -1);
807 window
->base
.size
= ImVec2(600, 700);
808 window
->base
.opened
= true;
809 window
->base
.display
= display_batch_window
;
810 window
->base
.destroy
= destroy_batch_window
;
812 window
->collapsed
= true;
813 window
->decode_cfg
= aub_viewer_decode_cfg();
815 aub_viewer_decode_ctx_init(&window
->decode_ctx
,
819 context
.file
->disasm
,
823 window
->decode_ctx
.display_shader
= batch_display_shader
;
824 window
->decode_ctx
.display_urb
= batch_display_urb
;
825 window
->decode_ctx
.edit_address
= batch_edit_address
;
827 update_batch_window(window
, false, exec_idx
);
829 list_addtail(&window
->base
.link
, &context
.windows
);
835 display_registers_window(struct window
*win
)
837 static struct ImGuiTextFilter filter
;
838 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
841 ImGui::BeginChild(ImGui::GetID("##block"));
842 hash_table_foreach(context
.file
->spec
->registers_by_name
, entry
) {
843 struct gen_group
*reg
= (struct gen_group
*) entry
->data
;
844 if (filter
.PassFilter(reg
->name
) &&
845 ImGui::CollapsingHeader(reg
->name
)) {
846 const struct gen_field
*field
= reg
->fields
;
848 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
857 show_register_window(void)
859 struct window
*window
= &context
.registers_window
;
861 if (window
->opened
) {
862 window
->opened
= false;
866 snprintf(window
->name
, sizeof(window
->name
), "Registers");
868 list_inithead(&window
->parent_link
);
869 window
->position
= ImVec2(-1, -1);
870 window
->size
= ImVec2(200, 400);
871 window
->opened
= true;
872 window
->display
= display_registers_window
;
873 window
->destroy
= destroy_window_noop
;
875 list_addtail(&window
->link
, &context
.windows
);
879 display_commands_window(struct window
*win
)
881 static struct ImGuiTextFilter cmd_filter
;
882 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
883 cmd_filter
.Draw("name filter");
884 static struct ImGuiTextFilter field_filter
;
885 field_filter
.Draw("field filter");
887 static char opcode_str
[9] = { 0, };
888 ImGui::InputText("opcode filter", opcode_str
, sizeof(opcode_str
),
889 ImGuiInputTextFlags_CharsHexadecimal
);
890 size_t opcode_len
= strlen(opcode_str
);
891 uint64_t opcode
= strtol(opcode_str
, NULL
, 16);
893 static bool show_dwords
= true;
894 if (ImGui::Button("Dwords")) show_dwords
^= 1;
896 ImGui::BeginChild(ImGui::GetID("##block"));
897 hash_table_foreach(context
.file
->spec
->commands
, entry
) {
898 struct gen_group
*cmd
= (struct gen_group
*) entry
->data
;
899 if ((cmd_filter
.PassFilter(cmd
->name
) &&
900 (opcode_len
== 0 || (opcode
& cmd
->opcode_mask
) == cmd
->opcode
)) &&
901 ImGui::CollapsingHeader(cmd
->name
)) {
902 const struct gen_field
*field
= cmd
->fields
;
903 int32_t last_dword
= -1;
905 if (show_dwords
&& field
->start
/ 32 != last_dword
) {
906 for (last_dword
= MAX2(0, last_dword
+ 1);
907 last_dword
< field
->start
/ 32; last_dword
++) {
908 ImGui::TextColored(context
.cfg
.dwords_color
,
909 "Dword %d", last_dword
);
911 ImGui::TextColored(context
.cfg
.dwords_color
, "Dword %d", last_dword
);
913 if (field_filter
.PassFilter(field
->name
))
914 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
919 hash_table_foreach(context
.file
->spec
->structs
, entry
) {
920 struct gen_group
*cmd
= (struct gen_group
*) entry
->data
;
921 if (cmd_filter
.PassFilter(cmd
->name
) && opcode_len
== 0 &&
922 ImGui::CollapsingHeader(cmd
->name
)) {
923 const struct gen_field
*field
= cmd
->fields
;
924 int32_t last_dword
= -1;
926 if (show_dwords
&& field
->start
/ 32 != last_dword
) {
927 last_dword
= field
->start
/ 32;
928 ImGui::TextColored(context
.cfg
.dwords_color
,
929 "Dword %d", last_dword
);
931 if (field_filter
.PassFilter(field
->name
))
932 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
941 show_commands_window(void)
943 struct window
*window
= &context
.commands_window
;
945 if (window
->opened
) {
946 window
->opened
= false;
950 snprintf(window
->name
, sizeof(window
->name
), "Commands & structs");
952 list_inithead(&window
->parent_link
);
953 window
->position
= ImVec2(-1, -1);
954 window
->size
= ImVec2(300, 400);
955 window
->opened
= true;
956 window
->display
= display_commands_window
;
957 window
->destroy
= destroy_window_noop
;
959 list_addtail(&window
->link
, &context
.windows
);
965 human_size(size_t size
)
967 unsigned divisions
= 0;
969 double divider
= 1024;
970 while (v
>= divider
) {
975 static const char *units
[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
976 static char result
[20];
977 snprintf(result
, sizeof(result
), "%.2f %s",
978 v
, divisions
>= ARRAY_SIZE(units
) ? "Too much!" : units
[divisions
]);
983 display_aubfile_window(struct window
*win
)
985 ImGuiColorEditFlags cflags
= (ImGuiColorEditFlags_NoAlpha
|
986 ImGuiColorEditFlags_NoLabel
|
987 ImGuiColorEditFlags_NoInputs
);
988 struct aub_viewer_cfg
*cfg
= &context
.cfg
;
990 ImGui::ColorEdit3("background", (float *)&cfg
->clear_color
, cflags
); ImGui::SameLine();
991 ImGui::ColorEdit3("missing", (float *)&cfg
->missing_color
, cflags
); ImGui::SameLine();
992 ImGui::ColorEdit3("error", (float *)&cfg
->error_color
, cflags
); ImGui::SameLine();
993 ImGui::ColorEdit3("highlight", (float *)&cfg
->highlight_color
, cflags
); ImGui::SameLine();
994 ImGui::ColorEdit3("dwords", (float *)&cfg
->dwords_color
, cflags
); ImGui::SameLine();
996 if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
997 if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
998 if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
1000 if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
1002 ImGui::Text("File name: %s", context
.input_file
);
1003 ImGui::Text("File size: %s", human_size(context
.file
->end
- context
.file
->map
));
1004 ImGui::Text("Execbufs %u", context
.file
->n_execs
);
1005 ImGui::Text("PCI ID: 0x%x", context
.file
->pci_id
);
1006 ImGui::Text("Application name: %s", context
.file
->app_name
);
1007 ImGui::Text(gen_get_device_name(context
.file
->pci_id
));
1009 ImGui::SetNextWindowContentWidth(500);
1010 if (ImGui::BeginPopupModal("Help", NULL
, ImGuiWindowFlags_AlwaysAutoResize
)) {
1011 ImGui::Text("Some global keybindings:");
1014 static const char *texts
[] = {
1015 "Ctrl-h", "show this screen",
1016 "Ctrl-c", "show commands list",
1017 "Ctrl-r", "show registers list",
1018 "Ctrl-b", "new batch window",
1019 "Ctrl-p/n", "switch to previous/next batch buffer",
1020 "Ctrl-Tab", "switch focus between window",
1021 "Ctrl-left/right", "align window to the side of the screen",
1024 for (uint32_t i
= 0; i
< ARRAY_SIZE(texts
); i
+= 2)
1025 align
= MAX2(align
, ImGui::CalcTextSize(texts
[i
]).x
);
1026 align
+= ImGui::GetStyle().WindowPadding
.x
+ 10;
1028 for (uint32_t i
= 0; i
< ARRAY_SIZE(texts
); i
+= 2) {
1029 ImGui::Text(texts
[i
]); ImGui::SameLine(align
); ImGui::Text(texts
[i
+ 1]);
1032 if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape
))
1033 ImGui::CloseCurrentPopup();
1039 show_aubfile_window(void)
1041 struct window
*window
= &context
.file_window
;
1046 snprintf(window
->name
, sizeof(window
->name
),
1047 "Aubinator Viewer: Intel AUB file decoder/editor");
1049 list_inithead(&window
->parent_link
);
1050 window
->size
= ImVec2(-1, 250);
1051 window
->position
= ImVec2(0, 0);
1052 window
->opened
= true;
1053 window
->display
= display_aubfile_window
;
1054 window
->destroy
= NULL
;
1056 list_addtail(&window
->link
, &context
.windows
);
1059 /* Main redrawing */
1062 display_windows(void)
1064 /* Start by disposing closed windows, we don't want to destroy windows that
1065 * have already been scheduled to be painted. So destroy always happens on
1066 * the next draw cycle, prior to any drawing.
1068 list_for_each_entry_safe(struct window
, window
, &context
.windows
, link
) {
1072 /* Can't close this one. */
1073 if (window
== &context
.file_window
) {
1074 window
->opened
= true;
1078 list_del(&window
->link
);
1079 list_del(&window
->parent_link
);
1080 if (window
->destroy
)
1081 window
->destroy(window
);
1084 list_for_each_entry(struct window
, window
, &context
.windows
, link
) {
1085 ImGui::SetNextWindowPos(window
->position
, ImGuiCond_FirstUseEver
);
1086 ImGui::SetNextWindowSize(window
->size
, ImGuiCond_FirstUseEver
);
1087 if (ImGui::Begin(window
->name
, &window
->opened
)) {
1088 window
->display(window
);
1089 window
->position
= ImGui::GetWindowPos();
1090 window
->size
= ImGui::GetWindowSize();
1092 if (window_has_ctrl_key('w'))
1093 window
->opened
= false;
1099 repaint_area(GtkGLArea
*area
, GdkGLContext
*gdk_gl_context
)
1101 ImGui_ImplOpenGL3_NewFrame();
1102 ImGui_ImplGtk3_NewFrame();
1110 glClearColor(context
.cfg
.clear_color
.Value
.x
,
1111 context
.cfg
.clear_color
.Value
.y
,
1112 context
.cfg
.clear_color
.Value
.z
, 1.0);
1113 glClear(GL_COLOR_BUFFER_BIT
);
1114 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1118 realize_area(GtkGLArea
*area
)
1120 ImGui::CreateContext();
1121 ImGui_ImplGtk3_Init(GTK_WIDGET(area
), true);
1122 ImGui_ImplOpenGL3_Init("#version 130");
1124 list_inithead(&context
.windows
);
1126 ImGui::StyleColorsDark();
1127 context
.cfg
= aub_viewer_cfg();
1129 ImGuiIO
& io
= ImGui::GetIO();
1130 io
.ConfigFlags
|= ImGuiConfigFlags_NavEnableKeyboard
;
1134 unrealize_area(GtkGLArea
*area
)
1136 gtk_gl_area_make_current(area
);
1138 ImGui_ImplOpenGL3_Shutdown();
1139 ImGui_ImplGtk3_Shutdown();
1140 ImGui::DestroyContext();
1144 size_allocate_area(GtkGLArea
*area
,
1145 GdkRectangle
*allocation
,
1148 if (!gtk_widget_get_realized(GTK_WIDGET(area
)))
1151 /* We want to catch only initial size allocate. */
1152 g_signal_handlers_disconnect_by_func(area
,
1153 (gpointer
) size_allocate_area
,
1155 show_aubfile_window();
1159 print_help(const char *progname
, FILE *file
)
1162 "Usage: %s [OPTION]... FILE\n"
1163 "Decode aub file contents from FILE.\n\n"
1164 " --help display this help and exit\n"
1165 " -x, --xml=DIR load hardware xml description from directory DIR\n",
1169 int main(int argc
, char *argv
[])
1173 const struct option aubinator_opts
[] = {
1174 { "help", no_argument
, (int *) &help
, true },
1175 { "xml", required_argument
, NULL
, 'x' },
1176 { NULL
, 0, NULL
, 0 }
1179 memset(&context
, 0, sizeof(context
));
1182 while ((c
= getopt_long(argc
, argv
, "x:s:", aubinator_opts
, &i
)) != -1) {
1185 context
.xml_path
= strdup(optarg
);
1193 context
.input_file
= argv
[optind
];
1195 if (help
|| !context
.input_file
) {
1196 print_help(argv
[0], stderr
);
1200 context
.file
= aub_file_open(context
.input_file
);
1202 gtk_init(NULL
, NULL
);
1204 context
.gtk_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1205 gtk_window_set_title(GTK_WINDOW(context
.gtk_window
), "Aubinator Viewer");
1206 g_signal_connect(context
.gtk_window
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
1207 gtk_window_resize(GTK_WINDOW(context
.gtk_window
), 1280, 720);
1209 GtkWidget
* gl_area
= gtk_gl_area_new();
1210 g_signal_connect(gl_area
, "render", G_CALLBACK(repaint_area
), NULL
);
1211 g_signal_connect(gl_area
, "realize", G_CALLBACK(realize_area
), NULL
);
1212 g_signal_connect(gl_area
, "unrealize", G_CALLBACK(unrealize_area
), NULL
);
1213 g_signal_connect(gl_area
, "size_allocate", G_CALLBACK(size_allocate_area
), NULL
);
1214 gtk_container_add(GTK_CONTAINER(context
.gtk_window
), gl_area
);
1216 gtk_widget_show_all(context
.gtk_window
);
1220 free(context
.xml_path
);
1222 return EXIT_SUCCESS
;