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