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
=
391 aub_mem_get_ppgtt_bo(mem
, address
);
393 FILE *f
= open_memstream(&window
->shader
, &window
->shader_size
);
395 gen_disasm_disassemble(context
.file
->disasm
, shader_bo
.map
, 0, f
);
400 list_addtail(&window
->base
.link
, &context
.windows
);
408 display_urb_window(struct window
*win
)
410 struct urb_window
*window
= (struct urb_window
*) win
;
411 static const char *stages
[] = {
412 [AUB_DECODE_STAGE_VS
] = "VS",
413 [AUB_DECODE_STAGE_HS
] = "HS",
414 [AUB_DECODE_STAGE_DS
] = "DS",
415 [AUB_DECODE_STAGE_GS
] = "GS",
416 [AUB_DECODE_STAGE_PS
] = "PS",
417 [AUB_DECODE_STAGE_CS
] = "CS",
420 ImGui::Text("URB allocation:");
421 window
->urb_view
.DrawAllocation("##urb",
422 ARRAY_SIZE(window
->urb_stages
),
423 window
->end_urb_offset
,
425 &window
->urb_stages
[0]);
429 destroy_urb_window(struct window
*win
)
431 struct urb_window
*window
= (struct urb_window
*) win
;
436 static struct urb_window
*
437 new_urb_window(struct aub_viewer_decode_ctx
*decode_ctx
, uint64_t address
)
439 struct urb_window
*window
= xtzalloc(*window
);
441 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
442 "URB view (0x%lx)##%p", address
, window
);
444 list_inithead(&window
->base
.parent_link
);
445 window
->base
.position
= ImVec2(-1, -1);
446 window
->base
.size
= ImVec2(700, 300);
447 window
->base
.opened
= true;
448 window
->base
.display
= display_urb_window
;
449 window
->base
.destroy
= destroy_urb_window
;
451 window
->end_urb_offset
= decode_ctx
->end_urb_offset
;
452 memcpy(window
->urb_stages
, decode_ctx
->urb_stages
, sizeof(window
->urb_stages
));
453 window
->urb_view
= AubinatorViewerUrb();
455 list_addtail(&window
->base
.link
, &context
.windows
);
460 /* Memory editor windows */
463 read_edit_window(const uint8_t *data
, size_t off
)
465 struct edit_window
*window
= (struct edit_window
*) data
;
467 return *((const uint8_t *) window
->gtt_bo
.map
+ window
->gtt_offset
+ off
);
471 write_edit_window(uint8_t *data
, size_t off
, uint8_t d
)
473 struct edit_window
*window
= (struct edit_window
*) data
;
474 uint8_t *gtt
= (uint8_t *) window
->gtt_bo
.map
+ window
->gtt_offset
+ off
;
475 uint8_t *aub
= (uint8_t *) window
->aub_bo
.map
+ window
->aub_offset
+ off
;
481 display_edit_window(struct window
*win
)
483 struct edit_window
*window
= (struct edit_window
*) win
;
485 if (window
->aub_bo
.map
&& window
->gtt_bo
.map
) {
486 ImGui::BeginChild(ImGui::GetID("##block"));
487 window
->editor
.DrawContents((uint8_t *) window
,
489 window
->gtt_bo
.size
- window
->gtt_offset
,
490 window
->aub_bo
.size
- window
->aub_offset
),
494 ImGui::Text("Memory view at 0x%lx not available", window
->address
);
499 destroy_edit_window(struct window
*win
)
501 struct edit_window
*window
= (struct edit_window
*) win
;
503 if (window
->aub_bo
.map
)
504 mprotect((void *) window
->aub_bo
.map
, 4096, PROT_READ
);
508 static struct edit_window
*
509 new_edit_window(struct aub_mem
*mem
, uint64_t address
, uint32_t len
)
511 struct edit_window
*window
= xtzalloc(*window
);
513 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
514 "Editing aub at 0x%lx##%p", address
, window
);
516 list_inithead(&window
->base
.parent_link
);
517 window
->base
.position
= ImVec2(-1, -1);
518 window
->base
.size
= ImVec2(500, 600);
519 window
->base
.opened
= true;
520 window
->base
.display
= display_edit_window
;
521 window
->base
.destroy
= destroy_edit_window
;
524 window
->address
= address
;
525 window
->aub_bo
= aub_mem_get_ppgtt_addr_aub_data(mem
, address
);
526 window
->gtt_bo
= aub_mem_get_ppgtt_addr_data(mem
, address
);
528 window
->editor
= MemoryEditor();
529 window
->editor
.OptShowDataPreview
= true;
530 window
->editor
.OptShowAscii
= false;
531 window
->editor
.ReadFn
= read_edit_window
;
532 window
->editor
.WriteFn
= write_edit_window
;
534 if (window
->aub_bo
.map
) {
535 uint64_t unaligned_map
= (uint64_t) window
->aub_bo
.map
;
536 window
->aub_bo
.map
= (const void *)(unaligned_map
& ~0xffful
);
537 window
->aub_offset
= unaligned_map
- (uint64_t) window
->aub_bo
.map
;
539 if (mprotect((void *) window
->aub_bo
.map
, window
->aub_bo
.size
, PROT_READ
| PROT_WRITE
) != 0) {
540 window
->aub_bo
.map
= NULL
;
544 window
->gtt_offset
= address
- window
->gtt_bo
.addr
;
546 list_addtail(&window
->base
.link
, &context
.windows
);
551 /* 4 level page table walk windows */
554 display_pml4_level(struct aub_mem
*mem
, uint64_t table_addr
, uint64_t table_virt_addr
, int level
)
559 struct gen_batch_decode_bo table_bo
=
560 aub_mem_get_phys_addr_data(mem
, table_addr
);
561 const uint64_t *table
= (const uint64_t *) ((const uint8_t *) table_bo
.map
+
562 table_addr
- table_bo
.addr
);
564 ImGui::TextColored(context
.cfg
.missing_color
, "Page not available");
568 uint64_t addr_increment
= 1ULL << (12 + 9 * (level
- 1));
571 for (int e
= 0; e
< 512; e
++) {
572 bool available
= (table
[e
] & 1) != 0;
573 uint64_t entry_virt_addr
= table_virt_addr
+ e
* addr_increment
;
576 ImGui::Text("Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx",
577 e
, table
[e
], entry_virt_addr
);
580 for (int e
= 0; e
< 512; e
++) {
581 bool available
= (table
[e
] & 1) != 0;
582 uint64_t entry_virt_addr
= table_virt_addr
+ e
* addr_increment
;
584 ImGui::TreeNodeEx(&table
[e
],
585 available
? ImGuiTreeNodeFlags_Framed
: 0,
586 "Entry%03i - phys_addr=0x%lx - virt_addr=0x%lx",
587 e
, table
[e
], entry_virt_addr
)) {
588 display_pml4_level(mem
, table
[e
] & ~0xffful
, entry_virt_addr
, level
-1);
596 display_pml4_window(struct window
*win
)
598 struct pml4_window
*window
= (struct pml4_window
*) win
;
600 ImGui::Text("pml4: %lx", window
->mem
->pml4
);
601 ImGui::BeginChild(ImGui::GetID("##block"));
602 display_pml4_level(window
->mem
, window
->mem
->pml4
, 0, 4);
607 show_pml4_window(struct pml4_window
*window
, struct aub_mem
*mem
)
609 if (window
->base
.opened
) {
610 window
->base
.opened
= false;
614 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
615 "4-Level page tables##%p", window
);
617 list_inithead(&window
->base
.parent_link
);
618 window
->base
.position
= ImVec2(-1, -1);
619 window
->base
.size
= ImVec2(500, 600);
620 window
->base
.opened
= true;
621 window
->base
.display
= display_pml4_window
;
622 window
->base
.destroy
= destroy_window_noop
;
626 list_addtail(&window
->base
.link
, &context
.windows
);
629 /* Batch decoding windows */
632 display_decode_options(struct aub_viewer_decode_cfg
*cfg
)
635 snprintf(name
, sizeof(name
), "command filter##%p", &cfg
->command_filter
);
636 cfg
->command_filter
.Draw(name
); ImGui::SameLine();
637 snprintf(name
, sizeof(name
), "field filter##%p", &cfg
->field_filter
);
638 cfg
->field_filter
.Draw(name
); ImGui::SameLine();
639 if (ImGui::Button("Dwords")) cfg
->show_dwords
^= 1;
643 batch_display_shader(void *user_data
, const char *shader_desc
, uint64_t address
)
645 struct batch_window
*window
= (struct batch_window
*) user_data
;
646 struct shader_window
*shader_window
=
647 new_shader_window(&window
->mem
, address
, shader_desc
);
649 list_add(&shader_window
->base
.parent_link
, &window
->base
.children_windows
);
653 batch_display_urb(void *user_data
, const struct aub_decode_urb_stage_state
*stages
)
655 struct batch_window
*window
= (struct batch_window
*) user_data
;
656 struct urb_window
*urb_window
= new_urb_window(&window
->decode_ctx
, 0);
658 list_add(&urb_window
->base
.parent_link
, &window
->base
.children_windows
);
662 batch_edit_address(void *user_data
, uint64_t address
, uint32_t len
)
664 struct batch_window
*window
= (struct batch_window
*) user_data
;
665 struct edit_window
*edit_window
=
666 new_edit_window(&window
->mem
, address
, len
);
668 list_add(&edit_window
->base
.parent_link
, &window
->base
.children_windows
);
671 static struct gen_batch_decode_bo
672 batch_get_bo(void *user_data
, uint64_t address
)
674 struct batch_window
*window
= (struct batch_window
*) user_data
;
676 if (window
->uses_ppgtt
)
677 return aub_mem_get_ppgtt_bo(&window
->mem
, address
);
679 return aub_mem_get_ggtt_bo(&window
->mem
, address
);
683 update_batch_window(struct batch_window
*window
, bool reset
, int exec_idx
)
686 aub_mem_fini(&window
->mem
);
687 aub_mem_init(&window
->mem
);
689 window
->exec_idx
= MAX2(MIN2(context
.file
->n_execs
- 1, exec_idx
), 0);
690 update_mem_for_exec(&window
->mem
, context
.file
, window
->exec_idx
);
694 display_batch_ring_write(void *user_data
, enum drm_i915_gem_engine_class engine
,
695 const void *data
, uint32_t data_len
)
697 struct batch_window
*window
= (struct batch_window
*) user_data
;
699 window
->uses_ppgtt
= false;
701 aub_viewer_render_batch(&window
->decode_ctx
, data
, data_len
, 0);
705 display_batch_execlist_write(void *user_data
,
706 enum drm_i915_gem_engine_class engine
,
707 uint64_t context_descriptor
)
709 struct batch_window
*window
= (struct batch_window
*) user_data
;
711 const uint32_t pphwsp_size
= 4096;
712 uint32_t pphwsp_addr
= context_descriptor
& 0xfffff000;
713 struct gen_batch_decode_bo pphwsp_bo
=
714 aub_mem_get_ggtt_bo(&window
->mem
, pphwsp_addr
);
715 uint32_t *context_img
= (uint32_t *)((uint8_t *)pphwsp_bo
.map
+
716 (pphwsp_addr
- pphwsp_bo
.addr
) +
719 uint32_t ring_buffer_head
= context_img
[5];
720 uint32_t ring_buffer_tail
= context_img
[7];
721 uint32_t ring_buffer_start
= context_img
[9];
722 uint32_t ring_buffer_length
= (context_img
[11] & 0x1ff000) + 4096;
724 window
->mem
.pml4
= (uint64_t)context_img
[49] << 32 | context_img
[51];
726 struct gen_batch_decode_bo ring_bo
=
727 aub_mem_get_ggtt_bo(&window
->mem
, ring_buffer_start
);
728 assert(ring_bo
.size
> 0);
729 void *commands
= (uint8_t *)ring_bo
.map
+ (ring_buffer_start
- ring_bo
.addr
) + ring_buffer_head
;
731 window
->uses_ppgtt
= true;
733 window
->decode_ctx
.engine
= engine
;
734 aub_viewer_render_batch(&window
->decode_ctx
, commands
,
735 MIN2(ring_buffer_tail
- ring_buffer_head
, ring_buffer_length
),
736 ring_buffer_start
+ ring_buffer_head
);
740 display_batch_window(struct window
*win
)
742 struct batch_window
*window
= (struct batch_window
*) win
;
744 ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
745 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
746 display_decode_options(&window
->decode_cfg
);
747 ImGui::PopItemWidth();
749 if (ImGui::InputInt("Execbuf", &window
->exec_idx
))
750 update_batch_window(window
, true, window
->exec_idx
);
752 if (window_has_ctrl_key('p'))
753 update_batch_window(window
, true, window
->exec_idx
- 1);
754 if (window_has_ctrl_key('n'))
755 update_batch_window(window
, true, window
->exec_idx
+ 1);
757 ImGui::Text("execbuf %i", window
->exec_idx
);
758 if (ImGui::Button("Show PPGTT")) { show_pml4_window(&window
->pml4_window
, &window
->mem
); }
760 ImGui::BeginChild(ImGui::GetID("##block"));
762 struct aub_read read
= {};
763 read
.user_data
= window
;
764 read
.ring_write
= display_batch_ring_write
;
765 read
.execlist_write
= display_batch_execlist_write
;
767 const uint8_t *iter
= context
.file
->execs
[window
->exec_idx
].start
;
768 while (iter
< context
.file
->execs
[window
->exec_idx
].end
) {
769 iter
+= aub_read_command(&read
, iter
,
770 context
.file
->execs
[window
->exec_idx
].end
- iter
);
777 destroy_batch_window(struct window
*win
)
779 struct batch_window
*window
= (struct batch_window
*) win
;
781 aub_mem_fini(&window
->mem
);
783 /* This works because children windows are inserted at the back of the
784 * list, ensuring the deletion loop goes through the children after calling
787 list_for_each_entry(struct window
, child_window
,
788 &window
->base
.children_windows
, parent_link
)
789 child_window
->opened
= false;
790 window
->pml4_window
.base
.opened
= false;
796 new_batch_window(int exec_idx
)
798 struct batch_window
*window
= xtzalloc(*window
);
800 snprintf(window
->base
.name
, sizeof(window
->base
.name
),
801 "Batch view##%p", window
);
803 list_inithead(&window
->base
.parent_link
);
804 list_inithead(&window
->base
.children_windows
);
805 window
->base
.position
= ImVec2(-1, -1);
806 window
->base
.size
= ImVec2(600, 700);
807 window
->base
.opened
= true;
808 window
->base
.display
= display_batch_window
;
809 window
->base
.destroy
= destroy_batch_window
;
811 window
->collapsed
= true;
812 window
->decode_cfg
= aub_viewer_decode_cfg();
814 aub_viewer_decode_ctx_init(&window
->decode_ctx
,
818 context
.file
->disasm
,
822 window
->decode_ctx
.display_shader
= batch_display_shader
;
823 window
->decode_ctx
.display_urb
= batch_display_urb
;
824 window
->decode_ctx
.edit_address
= batch_edit_address
;
826 update_batch_window(window
, false, exec_idx
);
828 list_addtail(&window
->base
.link
, &context
.windows
);
834 display_registers_window(struct window
*win
)
836 static struct ImGuiTextFilter filter
;
837 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
840 ImGui::BeginChild(ImGui::GetID("##block"));
841 hash_table_foreach(context
.file
->spec
->registers_by_name
, entry
) {
842 struct gen_group
*reg
= (struct gen_group
*) entry
->data
;
843 if (filter
.PassFilter(reg
->name
) &&
844 ImGui::CollapsingHeader(reg
->name
)) {
845 const struct gen_field
*field
= reg
->fields
;
847 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
856 show_register_window(void)
858 struct window
*window
= &context
.registers_window
;
860 if (window
->opened
) {
861 window
->opened
= false;
865 snprintf(window
->name
, sizeof(window
->name
), "Registers");
867 list_inithead(&window
->parent_link
);
868 window
->position
= ImVec2(-1, -1);
869 window
->size
= ImVec2(200, 400);
870 window
->opened
= true;
871 window
->display
= display_registers_window
;
872 window
->destroy
= destroy_window_noop
;
874 list_addtail(&window
->link
, &context
.windows
);
878 display_commands_window(struct window
*win
)
880 static struct ImGuiTextFilter cmd_filter
;
881 if (window_has_ctrl_key('f')) ImGui::SetKeyboardFocusHere();
882 cmd_filter
.Draw("name filter");
883 static struct ImGuiTextFilter field_filter
;
884 field_filter
.Draw("field filter");
886 static char opcode_str
[9] = { 0, };
887 ImGui::InputText("opcode filter", opcode_str
, sizeof(opcode_str
),
888 ImGuiInputTextFlags_CharsHexadecimal
);
889 size_t opcode_len
= strlen(opcode_str
);
890 uint64_t opcode
= strtol(opcode_str
, NULL
, 16);
892 static bool show_dwords
= true;
893 if (ImGui::Button("Dwords")) show_dwords
^= 1;
895 ImGui::BeginChild(ImGui::GetID("##block"));
896 hash_table_foreach(context
.file
->spec
->commands
, entry
) {
897 struct gen_group
*cmd
= (struct gen_group
*) entry
->data
;
898 if ((cmd_filter
.PassFilter(cmd
->name
) &&
899 (opcode_len
== 0 || (opcode
& cmd
->opcode_mask
) == cmd
->opcode
)) &&
900 ImGui::CollapsingHeader(cmd
->name
)) {
901 const struct gen_field
*field
= cmd
->fields
;
902 int32_t last_dword
= -1;
904 if (show_dwords
&& field
->start
/ 32 != last_dword
) {
905 for (last_dword
= MAX2(0, last_dword
+ 1);
906 last_dword
< field
->start
/ 32; last_dword
++) {
907 ImGui::TextColored(context
.cfg
.dwords_color
,
908 "Dword %d", last_dword
);
910 ImGui::TextColored(context
.cfg
.dwords_color
, "Dword %d", last_dword
);
912 if (field_filter
.PassFilter(field
->name
))
913 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
918 hash_table_foreach(context
.file
->spec
->structs
, entry
) {
919 struct gen_group
*cmd
= (struct gen_group
*) entry
->data
;
920 if (cmd_filter
.PassFilter(cmd
->name
) && opcode_len
== 0 &&
921 ImGui::CollapsingHeader(cmd
->name
)) {
922 const struct gen_field
*field
= cmd
->fields
;
923 int32_t last_dword
= -1;
925 if (show_dwords
&& field
->start
/ 32 != last_dword
) {
926 last_dword
= field
->start
/ 32;
927 ImGui::TextColored(context
.cfg
.dwords_color
,
928 "Dword %d", last_dword
);
930 if (field_filter
.PassFilter(field
->name
))
931 ImGui::Text("%s : %i -> %i\n", field
->name
, field
->start
, field
->end
);
940 show_commands_window(void)
942 struct window
*window
= &context
.commands_window
;
944 if (window
->opened
) {
945 window
->opened
= false;
949 snprintf(window
->name
, sizeof(window
->name
), "Commands & structs");
951 list_inithead(&window
->parent_link
);
952 window
->position
= ImVec2(-1, -1);
953 window
->size
= ImVec2(300, 400);
954 window
->opened
= true;
955 window
->display
= display_commands_window
;
956 window
->destroy
= destroy_window_noop
;
958 list_addtail(&window
->link
, &context
.windows
);
964 human_size(size_t size
)
966 unsigned divisions
= 0;
968 double divider
= 1024;
969 while (v
>= divider
) {
974 static const char *units
[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
975 static char result
[20];
976 snprintf(result
, sizeof(result
), "%.2f %s",
977 v
, divisions
>= ARRAY_SIZE(units
) ? "Too much!" : units
[divisions
]);
982 display_aubfile_window(struct window
*win
)
984 ImGuiColorEditFlags cflags
= (ImGuiColorEditFlags_NoAlpha
|
985 ImGuiColorEditFlags_NoLabel
|
986 ImGuiColorEditFlags_NoInputs
);
987 struct aub_viewer_cfg
*cfg
= &context
.cfg
;
989 ImGui::ColorEdit3("background", (float *)&cfg
->clear_color
, cflags
); ImGui::SameLine();
990 ImGui::ColorEdit3("missing", (float *)&cfg
->missing_color
, cflags
); ImGui::SameLine();
991 ImGui::ColorEdit3("error", (float *)&cfg
->error_color
, cflags
); ImGui::SameLine();
992 ImGui::ColorEdit3("highlight", (float *)&cfg
->highlight_color
, cflags
); ImGui::SameLine();
993 ImGui::ColorEdit3("dwords", (float *)&cfg
->dwords_color
, cflags
); ImGui::SameLine();
995 if (ImGui::Button("Commands list") || has_ctrl_key('c')) { show_commands_window(); } ImGui::SameLine();
996 if (ImGui::Button("Registers list") || has_ctrl_key('r')) { show_register_window(); } ImGui::SameLine();
997 if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
999 if (ImGui::Button("New batch window") || has_ctrl_key('b')) { new_batch_window(0); }
1001 ImGui::Text("File name: %s", context
.input_file
);
1002 ImGui::Text("File size: %s", human_size(context
.file
->end
- context
.file
->map
));
1003 ImGui::Text("Execbufs %u", context
.file
->n_execs
);
1004 ImGui::Text("PCI ID: 0x%x", context
.file
->pci_id
);
1005 ImGui::Text("Application name: %s", context
.file
->app_name
);
1006 ImGui::Text(gen_get_device_name(context
.file
->pci_id
));
1008 ImGui::SetNextWindowContentWidth(500);
1009 if (ImGui::BeginPopupModal("Help", NULL
, ImGuiWindowFlags_AlwaysAutoResize
)) {
1010 ImGui::Text("Some global keybindings:");
1013 static const char *texts
[] = {
1014 "Ctrl-h", "show this screen",
1015 "Ctrl-c", "show commands list",
1016 "Ctrl-r", "show registers list",
1017 "Ctrl-b", "new batch window",
1018 "Ctrl-p/n", "switch to previous/next batch buffer",
1019 "Ctrl-Tab", "switch focus between window",
1020 "Ctrl-left/right", "align window to the side of the screen",
1023 for (uint32_t i
= 0; i
< ARRAY_SIZE(texts
); i
+= 2)
1024 align
= MAX2(align
, ImGui::CalcTextSize(texts
[i
]).x
);
1025 align
+= ImGui::GetStyle().WindowPadding
.x
+ 10;
1027 for (uint32_t i
= 0; i
< ARRAY_SIZE(texts
); i
+= 2) {
1028 ImGui::Text(texts
[i
]); ImGui::SameLine(align
); ImGui::Text(texts
[i
+ 1]);
1031 if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape
))
1032 ImGui::CloseCurrentPopup();
1038 show_aubfile_window(void)
1040 struct window
*window
= &context
.file_window
;
1045 snprintf(window
->name
, sizeof(window
->name
),
1046 "Aubinator Viewer: Intel AUB file decoder/editor");
1048 list_inithead(&window
->parent_link
);
1049 window
->size
= ImVec2(-1, 250);
1050 window
->position
= ImVec2(0, 0);
1051 window
->opened
= true;
1052 window
->display
= display_aubfile_window
;
1053 window
->destroy
= NULL
;
1055 list_addtail(&window
->link
, &context
.windows
);
1058 /* Main redrawing */
1061 display_windows(void)
1063 /* Start by disposing closed windows, we don't want to destroy windows that
1064 * have already been scheduled to be painted. So destroy always happens on
1065 * the next draw cycle, prior to any drawing.
1067 list_for_each_entry_safe(struct window
, window
, &context
.windows
, link
) {
1071 /* Can't close this one. */
1072 if (window
== &context
.file_window
) {
1073 window
->opened
= true;
1077 list_del(&window
->link
);
1078 list_del(&window
->parent_link
);
1079 if (window
->destroy
)
1080 window
->destroy(window
);
1083 list_for_each_entry(struct window
, window
, &context
.windows
, link
) {
1084 ImGui::SetNextWindowPos(window
->position
, ImGuiCond_FirstUseEver
);
1085 ImGui::SetNextWindowSize(window
->size
, ImGuiCond_FirstUseEver
);
1086 if (ImGui::Begin(window
->name
, &window
->opened
)) {
1087 window
->display(window
);
1088 window
->position
= ImGui::GetWindowPos();
1089 window
->size
= ImGui::GetWindowSize();
1091 if (window_has_ctrl_key('w'))
1092 window
->opened
= false;
1098 repaint_area(GtkGLArea
*area
, GdkGLContext
*gdk_gl_context
)
1100 ImGui_ImplOpenGL3_NewFrame();
1101 ImGui_ImplGtk3_NewFrame();
1109 glClearColor(context
.cfg
.clear_color
.Value
.x
,
1110 context
.cfg
.clear_color
.Value
.y
,
1111 context
.cfg
.clear_color
.Value
.z
, 1.0);
1112 glClear(GL_COLOR_BUFFER_BIT
);
1113 ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
1117 realize_area(GtkGLArea
*area
)
1119 ImGui::CreateContext();
1120 ImGui_ImplGtk3_Init(GTK_WIDGET(area
), true);
1121 ImGui_ImplOpenGL3_Init("#version 130");
1123 list_inithead(&context
.windows
);
1125 ImGui::StyleColorsDark();
1126 context
.cfg
= aub_viewer_cfg();
1128 ImGuiIO
& io
= ImGui::GetIO();
1129 io
.ConfigFlags
|= ImGuiConfigFlags_NavEnableKeyboard
;
1133 unrealize_area(GtkGLArea
*area
)
1135 gtk_gl_area_make_current(area
);
1137 ImGui_ImplOpenGL3_Shutdown();
1138 ImGui_ImplGtk3_Shutdown();
1139 ImGui::DestroyContext();
1143 size_allocate_area(GtkGLArea
*area
,
1144 GdkRectangle
*allocation
,
1147 if (!gtk_widget_get_realized(GTK_WIDGET(area
)))
1150 /* We want to catch only initial size allocate. */
1151 g_signal_handlers_disconnect_by_func(area
,
1152 (gpointer
) size_allocate_area
,
1154 show_aubfile_window();
1158 print_help(const char *progname
, FILE *file
)
1161 "Usage: %s [OPTION]... FILE\n"
1162 "Decode aub file contents from FILE.\n\n"
1163 " --help display this help and exit\n"
1164 " -x, --xml=DIR load hardware xml description from directory DIR\n",
1168 int main(int argc
, char *argv
[])
1172 const struct option aubinator_opts
[] = {
1173 { "help", no_argument
, (int *) &help
, true },
1174 { "xml", required_argument
, NULL
, 'x' },
1175 { NULL
, 0, NULL
, 0 }
1178 memset(&context
, 0, sizeof(context
));
1181 while ((c
= getopt_long(argc
, argv
, "x:s:", aubinator_opts
, &i
)) != -1) {
1184 context
.xml_path
= strdup(optarg
);
1192 context
.input_file
= argv
[optind
];
1194 if (help
|| !context
.input_file
) {
1195 print_help(argv
[0], stderr
);
1199 context
.file
= aub_file_open(context
.input_file
);
1201 gtk_init(NULL
, NULL
);
1203 context
.gtk_window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1204 gtk_window_set_title(GTK_WINDOW(context
.gtk_window
), "Aubinator Viewer");
1205 g_signal_connect(context
.gtk_window
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
1206 gtk_window_resize(GTK_WINDOW(context
.gtk_window
), 1280, 720);
1208 GtkWidget
* gl_area
= gtk_gl_area_new();
1209 g_signal_connect(gl_area
, "render", G_CALLBACK(repaint_area
), NULL
);
1210 g_signal_connect(gl_area
, "realize", G_CALLBACK(realize_area
), NULL
);
1211 g_signal_connect(gl_area
, "unrealize", G_CALLBACK(unrealize_area
), NULL
);
1212 g_signal_connect(gl_area
, "size_allocate", G_CALLBACK(size_allocate_area
), NULL
);
1213 gtk_container_add(GTK_CONTAINER(context
.gtk_window
), gl_area
);
1215 gtk_widget_show_all(context
.gtk_window
);
1219 free(context
.xml_path
);
1221 return EXIT_SUCCESS
;