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