• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2023 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 <memory>
38 #include <string>
39 #include <vector>
40 
41 #include "util/list.h"
42 #include "util/macros.h"
43 
44 #include "common/intel_hang_dump.h"
45 
46 #include "intel_tools.h"
47 
48 /* Data */
49 
50 struct hang_bo {
51    void     *map    = NULL;
52    uint64_t  offset = 0;
53    uint64_t  size   = 0;
54 };
55 
56 struct hang_map {
57    uint64_t  offset = 0;
58    uint64_t  size   = 0;
59 };
60 
61 struct hang_exec {
62    uint64_t  offset = 0;
63 };
64 
65 /* UI */
66 
67 #include <epoxy/gl.h>
68 
69 #include "imgui/imgui.h"
70 #include "imgui/imgui_memory_editor.h"
71 #include "imgui_impl_gtk3.h"
72 #include "imgui_impl_opengl3.h"
73 
74 #include "aubinator_viewer.h"
75 
76 static int
map_key(int k)77 map_key(int k)
78 {
79    return ImGuiKey_COUNT + k;
80 }
81 
82 static bool
has_ctrl_key(int key)83 has_ctrl_key(int key)
84 {
85    return ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(map_key(key));
86 }
87 
88 static bool
window_has_ctrl_key(int key)89 window_has_ctrl_key(int key)
90 {
91    return ImGui::IsRootWindowOrAnyChildFocused() && has_ctrl_key(key);
92 }
93 
94 class window {
95 public:
96    virtual void display() = 0;
97    virtual void destroy() = 0;
98 
~window()99    virtual ~window() {}
100 
name() const101    const char *name() const { return m_name; }
102 
103    bool m_opened = true;
104 
105    ImVec2 m_position = ImVec2(-1, -1);
106    ImVec2 m_size     = ImVec2(700, 300);
107 
108 protected:
window()109    window() {}
110 
111    char m_name[128];
112 };
113 
114 static struct Context {
115    /* Hang file descriptor */
116    int file_fd = -1;
117    void *file_map = NULL;
118 
119    /* Map hang file in RW for edition */
120    bool edit = false;
121 
122    /* AUX-TT */
123    uint64_t aux_tt_addr = 0;
124 
125    struct intel_device_info devinfo;
126    struct intel_spec *spec = NULL;
127 
128    /* Result of parsing the hang file */
129    std::vector<hang_bo>   bos;
130    std::vector<hang_map>  maps;
131    std::vector<hang_exec> execs;
132 
133    hang_bo hw_image;
134 
135    GtkWidget *gtk_window;
136 
137    /* UI state*/
138    bool show_commands_window;
139    bool show_registers_window;
140 
141    struct aub_viewer_cfg cfg;
142 
143    std::vector<std::shared_ptr<window>> windows;
144 } context;
145 
146 thread_local ImGuiContext* __MesaImGui;
147 
find_bo(uint64_t addr)148 hang_bo *find_bo(uint64_t addr)
149 {
150    for (auto &bo : context.bos) {
151       if (addr >= bo.offset && addr < (bo.offset + bo.size))
152          return &bo;
153    }
154    return NULL;
155 }
156 
157 /**/
158 
159 static uint8_t
read_edit_window(const uint8_t * data,size_t off)160 read_edit_window(const uint8_t *data, size_t off)
161 {
162    return data[off];
163 }
164 
165 static void
write_edit_window(uint8_t * data,size_t off,uint8_t d)166 write_edit_window(uint8_t *data, size_t off, uint8_t d)
167 {
168    data[off] = d;
169 }
170 
171 class edit_window : public window {
172 public:
173    struct hang_bo m_bo;
174 
175    struct intel_batch_decode_bo m_aub_bo;
176    uint64_t m_aub_offset;
177 
178    struct intel_batch_decode_bo m_gtt_bo;
179    uint64_t m_gtt_offset;
180 
181    struct MemoryEditor m_editor;
182 
edit_window(const struct hang_bo & bo)183    edit_window(const struct hang_bo &bo)
184       : m_bo(bo) {
185       m_editor.OptShowDataPreview = true;
186       m_editor.OptShowAscii = false;
187       m_editor.ReadFn = read_edit_window;
188       m_editor.WriteFn = write_edit_window;
189 
190       snprintf(m_name, sizeof(m_name), "Memory view 0x%016" PRIx64 "##%p",
191                bo.offset, this);
192    }
193 
display()194    void display() {
195       if (m_bo.map) {
196          ImGui::BeginChild(ImGui::GetID("##block"));
197          m_editor.DrawContents((uint8_t *) m_bo.map, m_bo.size, m_bo.offset);
198          ImGui::EndChild();
199       } else {
200          ImGui::Text("Memory view at 0x%" PRIx64 " not available", m_bo.offset);
201       }
202    }
203 
destroy()204    void destroy() {}
205 };
206 
207 class shader_window : public window {
208 public:
209    std::string m_description;
210    uint64_t m_address;
211    std::string m_shader;
212 
shader_window(const char * description,uint64_t address)213    shader_window(const char *description, uint64_t address)
214       : m_description(description)
215       , m_address(address) {
216       snprintf(m_name, sizeof(m_name),
217                "%s (0x%" PRIx64 ")##%p", m_description.c_str(), m_address, this);
218 
219       hang_bo *bo = find_bo(address);
220       if (bo != NULL) {
221          char *shader_txt = NULL;
222          size_t shader_txt_size = 0;
223          FILE *f = open_memstream(&shader_txt, &shader_txt_size);
224          if (f) {
225             intel_disassemble(&context.devinfo,
226                               (const uint8_t *) bo->map +
227                               (address - bo->offset), 0, f);
228             fclose(f);
229          }
230 
231          m_shader = std::string(shader_txt);
232       }
233    }
234 
display()235    void display() {
236       ImGui::InputTextMultiline("Assembly",
237                                 (char *) m_shader.c_str(), m_shader.size(),
238                                 ImGui::GetContentRegionAvail(),
239                                 ImGuiInputTextFlags_ReadOnly);
240    }
241 
destroy()242    void destroy() {}
243 };
244 
245 class aux_tt_window : public window {
246 public:
aux_tt_window(uint64_t l3_addr)247    aux_tt_window(uint64_t l3_addr)
248       : m_l3_addr(l3_addr)
249       , m_bo(find_bo(l3_addr)) {
250       snprintf(m_name, sizeof(m_name), "AUX TT##%p", this);
251    }
252 
display()253    void display() {
254       ImGui::BeginChild(ImGui::GetID("##toplevel"));
255       if (m_bo != NULL)
256          display_level(3, m_l3_addr, 0);
257       else
258          ImGui::Text("AUX table buffer not found: 0x%" PRIx64,
259                      m_l3_addr);
260       ImGui::EndChild();
261    }
262 
destroy()263    void destroy() {}
264 
265 private:
display_level(int level,uint64_t table_addr,uint64_t base_addr)266    void display_level(int level, uint64_t table_addr, uint64_t base_addr) {
267       assert(level >= 1 && level <= 3);
268 
269       const hang_bo *bo =
270          table_addr == m_l3_addr ? m_bo : find_bo(table_addr);
271       if (bo == NULL) {
272          ImGui::Text("level %u not found addr=0x%016" PRIx64,
273                      level, table_addr);
274          return;
275       }
276 
277       static struct {
278          uint32_t top;
279          uint32_t bottom;
280       } levels[4] = {
281          {  0,  0, },
282          { 23, 16, },
283          { 35, 24, },
284          { 47, 36, },
285       };
286 
287       const uint64_t *entries =
288          (const uint64_t *)((const uint8_t *)bo->map + (table_addr - bo->offset));
289 
290       if (level == 1) {
291          uint32_t n_entries = context.devinfo.verx10 == 125 ? 16 : 256;
292          for (uint32_t i = 0; i < n_entries; i++) {
293             uint64_t addr = entries[i] & 0xffffffffff00ull;
294             ImGui::Text("entry%04u: addr=0x%012" PRIx64 " entry=0x%012" PRIx64
295                         " range=0x%012" PRIx64 "-0x%012" PRIx64,
296                         i, addr, entries[i],
297                         base_addr + (uint64_t)i << levels[level].bottom,
298                         base_addr + (uint64_t)i << levels[level].bottom);
299          }
300       } else {
301          for (uint32_t i = 0; i < 4096; i++) {
302             uint64_t entry_addr = base_addr + (uint64_t)i << levels[level].bottom;
303             uint64_t addr = entries[i] & 0xffffffff8000ull;
304             bool valid = (entries[i] & 0x1) != 0;
305             if (valid &&
306                 ImGui::TreeNodeEx(
307                    (void *)&entries[i],
308                    ImGuiTreeNodeFlags_Framed,
309                    "entry%04u: addr=0x%012" PRIx64 " entry=0x%012" PRIx64
310                    " range=0x%012" PRIx64 "-0x%012" PRIx64,
311                    i, addr, entries[i],
312                    entry_addr, entry_addr)) {
313                if (valid)
314                   display_level(level - 1, addr, entry_addr);
315                ImGui::TreePop();
316             }
317          }
318       }
319    }
320 
321    uint64_t m_l3_addr;
322    const hang_bo *m_bo;
323 };
324 
325 static struct intel_batch_decode_bo
batch_get_bo(void * user_data,bool ppgtt,uint64_t address)326 batch_get_bo(void *user_data, bool ppgtt, uint64_t address)
327 {
328    intel_batch_decode_bo ret_bo;
329    ret_bo.map = NULL;
330    ret_bo.addr = 0;
331 
332    if (!ppgtt)
333       return ret_bo;
334 
335    for (const auto &bo : context.bos) {
336       if (address >= bo.offset &&
337           address < (bo.offset + bo.size)) {
338          ret_bo.map = bo.map;
339          ret_bo.addr = bo.offset;
340          ret_bo.size = bo.size;
341       }
342    }
343 
344    return ret_bo;
345 }
346 
347 static void
batch_display_shader(void * user_data,const char * shader_desc,uint64_t address)348 batch_display_shader(void *user_data, const char *shader_desc, uint64_t address)
349 {
350    context.windows.push_back(std::shared_ptr<window>(new shader_window(shader_desc, address)));
351 }
352 
353 class batch_window : public window {
354 public:
batch_window(const struct hang_bo & bo)355    batch_window(const struct hang_bo &bo)
356       : m_bo(bo)
357       , m_collapsed(true) {
358       aub_viewer_decode_ctx_init(&m_decode_ctx,
359                                  &context.cfg,
360                                  &m_decode_cfg,
361                                  &context.devinfo,
362                                  context.spec,
363                                  batch_get_bo,
364                                  NULL,
365                                  NULL);
366       m_decode_ctx.display_shader = batch_display_shader;
367       // window->decode_ctx.display_urb = batch_display_urb;
368       // window->decode_ctx.edit_address = batch_edit_address;
369 
370       snprintf(m_name, sizeof(m_name), "Batch view 0x%016" PRIx64 "##%p",
371                bo.offset, this);
372    }
~batch_window()373    ~batch_window() {}
374 
display()375    void display() {
376          ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() / (2 * 2));
377          decode_options();
378          if (ImGui::Button("Edit commands"))
379             context.windows.push_back(std::shared_ptr<window>(new edit_window(m_bo)));
380          ImGui::PopItemWidth();
381 
382          ImGui::BeginChild(ImGui::GetID("##block"));
383 
384          aub_viewer_render_batch(&m_decode_ctx,
385                                  m_bo.map,
386                                  m_bo.size,
387                                  m_bo.offset,
388                                  false /* from_ring */);
389 
390          ImGui::EndChild();
391    }
392 
destroy()393    void destroy() {}
394 
395 private:
396 
decode_options()397    void decode_options() {
398       char name[40];
399       snprintf(name, sizeof(name), "command filter##%p", &m_decode_cfg.command_filter);
400       m_decode_cfg.command_filter.Draw(name); ImGui::SameLine();
401       snprintf(name, sizeof(name), "field filter##%p", &m_decode_cfg.field_filter);
402       m_decode_cfg.field_filter.Draw(name); ImGui::SameLine();
403       if (ImGui::Button("Dwords")) m_decode_cfg.show_dwords ^= 1;
404    }
405 
406    struct hang_bo m_bo;
407 
408    bool m_collapsed;
409 
410    struct aub_viewer_decode_cfg m_decode_cfg;
411    struct aub_viewer_decode_ctx m_decode_ctx;
412 
413    char edit_address[20];
414 };
415 
416 /* Main window */
417 
418 static const char *
human_size(size_t size)419 human_size(size_t size)
420 {
421    unsigned divisions = 0;
422    double v = size;
423    double divider = 1024;
424    while (v >= divider) {
425       v /= divider;
426       divisions++;
427    }
428 
429    static const char *units[] = { "Bytes", "Kilobytes", "Megabytes", "Gigabytes" };
430    static char result[20];
431    snprintf(result, sizeof(result), "%.2f %s",
432             v, divisions >= ARRAY_SIZE(units) ? "Too much!" : units[divisions]);
433    return result;
434 }
435 
436 static void
display_hang_stats()437 display_hang_stats()
438 {
439    ImGui::Begin("Hang stats");
440 
441    ImGuiColorEditFlags cflags = (ImGuiColorEditFlags_NoAlpha |
442                                  ImGuiColorEditFlags_NoLabel |
443                                  ImGuiColorEditFlags_NoInputs);
444    struct aub_viewer_cfg *cfg = &context.cfg;
445 
446    ImGui::ColorEdit3("background", (float *)&cfg->clear_color, cflags); ImGui::SameLine();
447    ImGui::ColorEdit3("missing", (float *)&cfg->missing_color, cflags); ImGui::SameLine();
448    ImGui::ColorEdit3("error", (float *)&cfg->error_color, cflags); ImGui::SameLine();
449    ImGui::ColorEdit3("highlight", (float *)&cfg->highlight_color, cflags); ImGui::SameLine();
450    ImGui::ColorEdit3("dwords", (float *)&cfg->dwords_color, cflags); ImGui::SameLine();
451    ImGui::ColorEdit3("booleans", (float *)&cfg->boolean_color, cflags); ImGui::SameLine();
452 
453    if (ImGui::Button("Help") || has_ctrl_key('h')) { ImGui::OpenPopup("Help"); }
454 
455    ImGui::Text("BOs:        %zu", context.bos.size());
456    ImGui::Text("Execs       %zu", context.execs.size());
457    ImGui::Text("Maps:       %zu", context.maps.size());
458    ImGui::Text("PCI ID:    0x%x", context.devinfo.pci_device_id);
459 
460    ImGui::SetNextWindowContentWidth(500);
461    if (ImGui::BeginPopupModal("Help", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
462       ImGui::Text("Some global keybindings:");
463       ImGui::Separator();
464 
465       static const char *texts[] = {
466          "Ctrl-h",          "show this screen",
467          "Ctrl-c",          "show commands list",
468          "Ctrl-r",          "show registers list",
469          "Ctrl-b",          "new batch window",
470          "Ctrl-p/n",        "switch to previous/next batch buffer",
471          "Ctrl-Tab",        "switch focus between window",
472          "Ctrl-left/right", "align window to the side of the screen",
473       };
474       float align = 0.0f;
475       for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2)
476          align = MAX2(align, ImGui::CalcTextSize(texts[i]).x);
477       align += ImGui::GetStyle().WindowPadding.x + 10;
478 
479       for (uint32_t i = 0; i < ARRAY_SIZE(texts); i += 2) {
480          ImGui::Text("%s", texts[i]); ImGui::SameLine(align); ImGui::Text("%s", texts[i + 1]);
481       }
482 
483       if (ImGui::Button("Done") || ImGui::IsKeyPressed(ImGuiKey_Escape))
484          ImGui::CloseCurrentPopup();
485       ImGui::EndPopup();
486    }
487 
488    uint64_t exec_buf_addr = 0;
489    if (!context.execs.empty())
490       exec_buf_addr = context.execs.front().offset;
491 
492    ImGui::BeginChild(ImGui::GetID("BO list:"));
493    for (const auto &bo : context.bos) {
494       char bo_name[80];
495       snprintf(bo_name, sizeof(bo_name),
496                "BO 0x%012" PRIx64 "-0x%012" PRIx64 " size=%" PRIu64 "(%s) %s",
497                bo.offset, bo.offset + bo.size - 1, bo.size, human_size(bo.size),
498                bo.offset == exec_buf_addr ? "BATCH BUFFER" : "");
499 
500       if (ImGui::Selectable(bo_name, false))
501          context.windows.push_back(std::shared_ptr<window>(new batch_window(bo)));
502    }
503    if (context.hw_image.size != 0 && ImGui::Selectable("HW IMAGE", false))
504       context.windows.push_back(std::shared_ptr<window>(new batch_window(context.hw_image)));
505    if (context.aux_tt_addr != 0 && ImGui::Selectable("AUX-TT", false))
506       context.windows.push_back(std::shared_ptr<window>(new aux_tt_window(context.aux_tt_addr)));
507    ImGui::EndChild();
508 
509    ImGui::End();
510 }
511 
512 /* Main redrawing */
513 
514 static void
display_windows(void)515 display_windows(void)
516 {
517    display_hang_stats();
518 
519    /* Start by disposing closed windows, we don't want to destroy windows that
520     * have already been scheduled to be painted. So destroy always happens on
521     * the next draw cycle, prior to any drawing.
522     */
523    auto it = context.windows.begin();
524    while (it != context.windows.end()) {
525       if (!(*it)->m_opened) {
526          (*it)->destroy();
527          it = context.windows.erase(it);
528       } else {
529          it++;
530       }
531    }
532 
533    for (uint32_t i = 0; i < context.windows.size(); i++) {
534       std::shared_ptr<window> window = context.windows[i];
535       ImGui::SetNextWindowPos(window->m_position, ImGuiCond_FirstUseEver);
536       ImGui::SetNextWindowSize(window->m_size, ImGuiCond_FirstUseEver);
537       if (ImGui::Begin(window->name(), &window->m_opened)) {
538          window->display();
539          window->m_position = ImGui::GetWindowPos();
540          window->m_size = ImGui::GetWindowSize();
541       }
542       if (window_has_ctrl_key('w'))
543          window->m_opened = false;
544       ImGui::End();
545    }
546 }
547 
548 static void
repaint_area(GtkGLArea * area,GdkGLContext * gdk_gl_context)549 repaint_area(GtkGLArea *area, GdkGLContext *gdk_gl_context)
550 {
551    ImGui_ImplOpenGL3_NewFrame();
552    ImGui_ImplGtk3_NewFrame();
553    ImGui::NewFrame();
554 
555    display_windows();
556 
557    ImGui::EndFrame();
558    ImGui::Render();
559 
560    glClearColor(context.cfg.clear_color.Value.x,
561                 context.cfg.clear_color.Value.y,
562                 context.cfg.clear_color.Value.z, 1.0);
563    glClear(GL_COLOR_BUFFER_BIT);
564    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
565 }
566 
567 static void
realize_area(GtkGLArea * area)568 realize_area(GtkGLArea *area)
569 {
570    ImGui::CreateContext();
571    ImGui_ImplGtk3_Init(GTK_WIDGET(area), true);
572    ImGui_ImplOpenGL3_Init("#version 130");
573 
574    ImGui::StyleColorsDark();
575    context.cfg = aub_viewer_cfg();
576 
577    ImGuiIO& io = ImGui::GetIO();
578    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
579 }
580 
581 static void
unrealize_area(GtkGLArea * area)582 unrealize_area(GtkGLArea *area)
583 {
584    gtk_gl_area_make_current(area);
585 
586    ImGui_ImplOpenGL3_Shutdown();
587    ImGui_ImplGtk3_Shutdown();
588    ImGui::DestroyContext();
589 }
590 
591 static void
size_allocate_area(GtkGLArea * area,GdkRectangle * allocation,gpointer user_data)592 size_allocate_area(GtkGLArea *area,
593                    GdkRectangle *allocation,
594                    gpointer user_data)
595 {
596    if (!gtk_widget_get_realized(GTK_WIDGET(area)))
597       return;
598 
599    /* We want to catch only initial size allocate. */
600    g_signal_handlers_disconnect_by_func(area,
601                                         (gpointer) size_allocate_area,
602                                         user_data);
603    // TODO
604 }
605 
606 static void
print_help(const char * progname,FILE * file)607 print_help(const char *progname, FILE *file)
608 {
609    fprintf(file,
610            "Usage: %s -p platform HANG_FILE\n"
611            "\n"
612            "    -p, --platform platform    platform to use for decoding\n"
613            "    -e, --edit                 map the hang file read/write for edition\n"
614            "    -x, --aux-tt                 map the hang file read/write for edition\n"
615            , progname);
616 }
617 
618 static void
add_bo(void * map,uint64_t addr,uint64_t size)619 add_bo(void *map, uint64_t addr, uint64_t size)
620 {
621    hang_bo bo;
622    bo.map    = map;
623    bo.offset = addr;
624    bo.size   = size;
625    context.bos.push_back(bo);
626 }
627 
628 static void
add_map(uint64_t addr,uint64_t size)629 add_map(uint64_t addr, uint64_t size)
630 {
631    hang_map map;
632    map.offset = addr;
633    map.size   = size;
634    context.maps.push_back(map);
635 }
636 
637 static void
add_exec(uint64_t addr)638 add_exec(uint64_t addr)
639 {
640    hang_exec exec;
641    exec.offset = addr;
642    context.execs.push_back(exec);
643 }
644 
645 static size_t
get_block_size(uint32_t type)646 get_block_size(uint32_t type)
647 {
648    switch (type) {
649    case INTEL_HANG_DUMP_BLOCK_TYPE_HEADER:   return sizeof(struct intel_hang_dump_block_header);
650    case INTEL_HANG_DUMP_BLOCK_TYPE_BO:       return sizeof(struct intel_hang_dump_block_bo);
651    case INTEL_HANG_DUMP_BLOCK_TYPE_MAP:      return sizeof(struct intel_hang_dump_block_map);
652    case INTEL_HANG_DUMP_BLOCK_TYPE_EXEC:     return sizeof(struct intel_hang_dump_block_exec);
653    case INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE: return sizeof(struct intel_hang_dump_block_hw_image);
654    default:                                  unreachable("invalid block");
655    }
656 }
657 
658 static void
parse_hang_file(const char * filename)659 parse_hang_file(const char *filename)
660 {
661    context.file_fd = open(filename, context.edit ? O_RDWR : O_RDONLY);
662    if (context.file_fd < 0)
663       exit(EXIT_FAILURE);
664 
665    struct stat file_stats;
666    if (fstat(context.file_fd, &file_stats) != 0)
667       exit(EXIT_FAILURE);
668 
669    context.file_map = mmap(NULL, file_stats.st_size,
670                            PROT_READ | PROT_WRITE,
671                            context.edit ? MAP_SHARED : MAP_PRIVATE,
672                            context.file_fd, 0);
673    if (context.file_map == MAP_FAILED)
674       exit(EXIT_FAILURE);
675 
676    uint8_t *current_file_ptr = (uint8_t *) context.file_map;
677    uint8_t *last_file_ptr = current_file_ptr + file_stats.st_size;
678 
679    while (current_file_ptr < last_file_ptr) {
680       union intel_hang_dump_block_all *block_header =
681          (union intel_hang_dump_block_all *)current_file_ptr;
682       size_t block_size = get_block_size(block_header->base.type);
683 
684       switch (block_header->base.type) {
685       case INTEL_HANG_DUMP_BLOCK_TYPE_HEADER:
686          assert(block_header->header.magic == INTEL_HANG_DUMP_MAGIC);
687          assert(block_header->header.version == INTEL_HANG_DUMP_VERSION);
688          break;
689 
690       case INTEL_HANG_DUMP_BLOCK_TYPE_BO: {
691          add_bo((uint8_t *) current_file_ptr + block_size,
692                 block_header->bo.offset,
693                 block_header->bo.size);
694          current_file_ptr = (uint8_t *) current_file_ptr + block_size + block_header->bo.size;
695          break;
696       }
697 
698       case INTEL_HANG_DUMP_BLOCK_TYPE_HW_IMAGE: {
699          context.hw_image.offset = block_header->bo.offset;
700          context.hw_image.size = block_header->hw_img.size;
701          context.hw_image.map = (uint8_t *) current_file_ptr + block_size;
702          current_file_ptr = (uint8_t *) current_file_ptr + block_size + block_header->hw_img.size;
703          break;
704       }
705 
706       case INTEL_HANG_DUMP_BLOCK_TYPE_MAP: {
707          add_map(block_header->map.offset,
708                  block_header->map.size);
709          current_file_ptr = (uint8_t *) current_file_ptr + block_size;
710          break;
711       }
712 
713       case INTEL_HANG_DUMP_BLOCK_TYPE_EXEC: {
714          add_exec(block_header->exec.offset);
715          current_file_ptr = (uint8_t *) current_file_ptr + block_size;
716          break;
717       }
718 
719       default:
720          unreachable("Invalid block type");
721       }
722    }
723 }
724 
725 int
main(int argc,char * argv[])726 main(int argc, char *argv[])
727 {
728    int c, i;
729    bool help = false;
730    const char *platform = NULL;
731    const struct option aubinator_opts[] = {
732       { "platform",      required_argument, NULL,                          'p'  },
733       { "aux-tt",        required_argument, NULL,                          'x'  },
734       { "edit",          no_argument,       NULL,                          'e'  },
735       { "help",          no_argument,       (int *) &help,                 true },
736       { NULL,            0,                 NULL,                          0    },
737    };
738 
739    context = {};
740 
741    i = 0;
742    while ((c = getopt_long(argc, argv, "p:ex:", aubinator_opts, &i)) != -1) {
743       switch (c) {
744       case 'p':
745          platform = optarg;
746          break;
747       case 'e':
748          context.edit = true;
749          break;
750       case 'x':
751          context.aux_tt_addr = strtoll(optarg, NULL, 16);
752          break;
753       default:
754          break;
755       }
756    }
757 
758    const char *filename = NULL;
759    if (optind < argc)
760       filename = argv[optind];
761 
762    if (help || !platform || !filename) {
763       print_help(argv[0], stderr);
764       exit(0);
765    }
766 
767    intel_get_device_info_from_pci_id(
768       intel_device_name_to_pci_device_id(platform),
769       &context.devinfo);
770 
771    context.spec = intel_spec_load(&context.devinfo);
772 
773    parse_hang_file(filename);
774 
775    gtk_init(NULL, NULL);
776 
777    context.gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
778    gtk_window_set_title(GTK_WINDOW(context.gtk_window), "Hang Viewer");
779    g_signal_connect(context.gtk_window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
780    gtk_window_resize(GTK_WINDOW(context.gtk_window), 1280, 720);
781 
782    GtkWidget* gl_area = gtk_gl_area_new();
783    g_signal_connect(gl_area, "render", G_CALLBACK(repaint_area), NULL);
784    g_signal_connect(gl_area, "realize", G_CALLBACK(realize_area), NULL);
785    g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize_area), NULL);
786    g_signal_connect(gl_area, "size_allocate", G_CALLBACK(size_allocate_area), NULL);
787    gtk_container_add(GTK_CONTAINER(context.gtk_window), gl_area);
788 
789    gtk_widget_show_all(context.gtk_window);
790 
791    gtk_main();
792 
793    return EXIT_SUCCESS;
794 }
795