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