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