diff --git a/.gitignore b/.gitignore index 44c25d0..582e4b6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ build/ *.swo *.swp rntviewer -*.gdbhist +.gdbhist +.gdb_history diff --git a/Makefile b/Makefile index a90c655..724e248 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,10 @@ build/imgui.o: src/imgui_inc.cpp third_party/imgui/imgui.h $(CXX) -O3 -fPIC -c -Ithird_party/imgui $< -o $@ d: - $(MOLD) $(CXX) -DDEBUG -g -O0 $(CFLAGS) -fsanitize=undefined $(INC) $(ROOTFLAGS) -o rntviewer src/rntviewer.cpp build/imgui.o -lasan $(ROOTLIBS) $(LIBS) + $(MOLD) $(CXX) -DDEBUG -g -O0 -DENABLE_ASAN $(CFLAGS) -fsanitize=undefined $(INC) $(ROOTFLAGS) -o rntviewer src/rntviewer.cpp build/imgui.o -lasan $(ROOTLIBS) $(LIBS) + +noasan: + $(MOLD) $(CXX) -DDEBUG -g -O0 $(CFLAGS) -fsanitize=undefined $(INC) $(ROOTFLAGS) -o rntviewer src/rntviewer.cpp build/imgui.o $(ROOTLIBS) $(LIBS) r: $(MOLD) $(CXX) -O2 $(CFLAGS) $(INC) $(ROOTFLAGS) -o rntviewer src/rntviewer.cpp build/imgui.o $(ROOTLIBS) $(LIBS) diff --git a/imgui.ini b/imgui.ini index ff85fd1..47ace67 100644 --- a/imgui.ini +++ b/imgui.ini @@ -8,7 +8,7 @@ Size=1152,1414 [Window][main] Pos=0,0 -Size=1143,1388 +Size=1775,680 [Window][Hex View] Pos=91,62 diff --git a/src/app_state.h b/src/app_state.h index f2cfc70..6891964 100644 --- a/src/app_state.h +++ b/src/app_state.h @@ -1,57 +1,23 @@ -enum Input_Key { - KEY_UP, - KEY_DOWN, - KEY_LEFT, - KEY_RIGHT, - KEY_Q, - KEY_ALT, - KEY_TAB, - - KEY_COUNT -}; - -enum Mouse_Button { - MOUSE_BTN_LEFT, - MOUSE_BTN_RIGHT, - - MOUSE_BTN_COUNT -}; - -enum Mouse_Button_State : u16 { - MOUSE_BTN_STATE_IS_DOWN = 0x1, - MOUSE_BTN_STATE_JUST_PRESSED = 0x2, - MOUSE_BTN_STATE_JUST_RELEASED = 0x4, -}; - -enum Key_State : u16 { - KEY_STATE_IS_DOWN = 0x1, - KEY_STATE_JUST_PRESSED = 0x2, - KEY_STATE_JUST_RELEASED = 0x4, -}; - -struct User_Input { - u16 key_state[KEY_COUNT]; - - u16 mouse_btn_state[MOUSE_BTN_COUNT]; - struct { i32 x; i32 y; } mouse_pos; -}; - -struct Window_Data { - // Real width and height of the window - i32 width; - i32 height; - bool size_just_changed; - - f32 desired_aspect_ratio; -}; - struct App_State { Window_Data win_data; User_Input user_input; + RNTuple_Info *rntinfo; FILE *inspected_file; u8 *inspected_fmem; u64 inspected_file_size; // @Platform: inotify file descriptor int inot; + + const char *ntpl_name; }; + +internal +size_t file_size(FILE *f) +{ + fseek(f, 0, SEEK_END); + size_t res = ftell(f); + fseek(f, 0, SEEK_SET); + return res; +} + diff --git a/src/mainloop.cpp b/src/mainloop.cpp index 062cf4e..3b4c1b6 100644 --- a/src/mainloop.cpp +++ b/src/mainloop.cpp @@ -1,12 +1,3 @@ -internal -size_t file_size(FILE *f) -{ - fseek(f, 0, SEEK_END); - size_t res = ftell(f); - fseek(f, 0, SEEK_SET); - return res; -} - internal void monitor_key(GLFWwindow *window, u16 *key_state, i32 glfw_key, Input_Key haru_key) { u16& state = key_state[haru_key]; diff --git a/src/mem.cpp b/src/mem.cpp index 1418cf1..f4e0602 100644 --- a/src/mem.cpp +++ b/src/mem.cpp @@ -4,7 +4,7 @@ #define SLLStackPush_N(f,n,next) ((n)->next=(f), (f)=(n)) #define SLLStackPop_N(f,next) ((f)=(f)->next) -#ifdef DEBUG +#ifdef ENABLE_ASAN #define asan_poison_memory_region(mem, cmt) __asan_poison_memory_region(mem, cmt) #define asan_unpoison_memory_region(mem, cmt) __asan_unpoison_memory_region(mem, cmt) #else @@ -12,27 +12,6 @@ #define asan_unpoison_memory_region(mem, cmt) #endif -// @Platform -internal -void *os_reserve(u64 size) -{ - return mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); -} - -// @Platform -internal -void os_release(void *mem, u64 size) -{ - munmap(mem, size); -} - -// @Platform -internal -b32x os_commit(void *addr, u64 size) -{ - return mprotect(addr, size, PROT_READ|PROT_WRITE) == 0; -} - // init_res: initial reserved size // init_cmt: initial committed size internal @@ -143,6 +122,15 @@ T *arena_push(Arena *arena) return (T *)mem; } +template +internal +T *arena_push_zeroed(Arena *arena) +{ + T *mem = arena_push(arena); + memset(mem, 0, sizeof(T)); + return mem; +} + template internal T *arena_push_array_no_zero(Arena *arena, u64 count) @@ -181,7 +169,7 @@ u64 arena_pos(Arena *arena) internal void arena_pop_to(Arena *arena, u64 big_pos_unclamped) { - u64 big_pos = min(ARENA_HEADER_SIZE, big_pos_unclamped); + u64 big_pos = max(ARENA_HEADER_SIZE, big_pos_unclamped); // unroll the chain Arena *current = arena->cur; diff --git a/src/platform_linux.h b/src/platform_linux.h new file mode 100644 index 0000000..80b2f0b --- /dev/null +++ b/src/platform_linux.h @@ -0,0 +1,65 @@ +#include +#include +#include +#include // for NAME_MAX + +internal +void os_open_and_map_file(const char *fname, App_State &app) +{ + FILE *file = fopen(fname, "rb"); + int fd = fileno(file); + size_t fsize = file_size(file); + + void *fmem = mmap(0, fsize, PROT_READ, MAP_SHARED_VALIDATE, fd, 0); + if (!fmem) + fprintf(stderr, "Failed to open file %s\n", fname); + + app.inspected_file = file; + app.inspected_file_size = fsize; + app.inspected_fmem = reinterpret_cast(fmem); +} + +internal +void os_unmap_file(u8 *&mem, u64 size) +{ + if (mem) munmap(mem, size); + mem = nullptr; +} + +internal +void os_start_file_watch(const char *fname, App_State &app) +{ + int inot = inotify_init1(IN_NONBLOCK); + if (inot == -1) + fprintf(stderr, "Failed to init inotify: %s (%d)\n", strerror(errno), errno); + if (inotify_add_watch(inot, fname, IN_MODIFY) == -1) + fprintf(stderr, "Failed to add inotify watch: %s (%d)\n", strerror(errno), errno); + + app.inot = inot; +} + +internal +void os_stop_file_watch(App_State &app) +{ + if (app.inot != -1) close(app.inot); + app.inot = -1; +} + +internal +void *os_reserve(u64 size) +{ + return mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +} + +internal +void os_release(void *mem, u64 size) +{ + munmap(mem, size); +} + +internal +b32x os_commit(void *addr, u64 size) +{ + return mprotect(addr, size, PROT_READ|PROT_WRITE) == 0; +} + diff --git a/src/render.cpp b/src/render.cpp index f64336e..7148b8d 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -1,34 +1,53 @@ internal -MemoryEditor make_memory_editor() +u32 mem_edit_bg_color_fn(const u8 *data, u64 off, const void *user_data) +{ + const RNTuple_Info *ntinfo = reinterpret_cast(user_data); + + if (ntinfo->header.start <= off && off <= ntinfo->header.end()) + return IM_COL32(200, 0, 50, 255); + if (ntinfo->footer.start <= off && off <= ntinfo->footer.end()) + return IM_COL32(50, 0, 200, 255); + return IM_COL32(0, 0, 0, 0); +} + +internal +MemoryEditor make_memory_editor(const RNTuple_Info &ntpl_info) { MemoryEditor mem_edit; // mem_edit.ReadOnly = true; mem_edit.Cols = 32; + mem_edit.BgColorFn = mem_edit_bg_color_fn; + mem_edit.BgColorFnUserData = &ntpl_info; return mem_edit; } internal void update_and_render(Arena *arena, App_State &app, f32 delta_time) { + (void)delta_time; + ImGui::SetNextWindowPos({ 0, 0 }); ImGui::SetNextWindowSize({ (f32)app.win_data.width, (f32)app.win_data.height }); Temp scratch = temp_begin(arena); defer { temp_end(scratch); }; - u64 text_buf_size = min(app.inspected_file_size * 2, 2048); + const u64 inspected_max_size = 2048; + u64 text_buf_size = min(app.inspected_file_size * 2, inspected_max_size); char *text_buf = arena_push_array_no_zero(scratch.arena, text_buf_size + 1); - // TODO: convert file content to human readable + // Convert file content to human readable + // @Speed: maybe do this only when the file changes for (u64 i = 0; i < text_buf_size / 2; ++i) sprintf(&text_buf[2 * i], "%02X", app.inspected_fmem[i]); text_buf[text_buf_size] = 0; if (ImGui::Begin("main")) { - // ImGui::SetNextWindowContentSize({ 500, 600 }); - // ImGui::SetCursorX(30); - // ImGui::TextWrapped("%s", text_buf); - static MemoryEditor mem_edit = make_memory_editor(); + ImGui::Text("Inspecting RNTuple '%s' (%s)", app.ntpl_name, + (const char *)rntuple_description(scratch.arena, app.rntinfo->anchor)); + + static MemoryEditor mem_edit = make_memory_editor(*app.rntinfo); mem_edit.DrawWindow("Hex View", app.inspected_fmem, app.inspected_file_size); + ImGui::End(); } } diff --git a/src/rntuple.cpp b/src/rntuple.cpp new file mode 100644 index 0000000..e7af72b --- /dev/null +++ b/src/rntuple.cpp @@ -0,0 +1,38 @@ +internal +String8 rntuple_description(Arena *arena, const RNTuple &anchor) +{ + String8 desc = push_str8f(arena, "version %u.%u.%u.%u", + anchor.GetVersionEpoch(), + anchor.GetVersionMajor(), + anchor.GetVersionMinor(), + anchor.GetVersionPatch()); + return desc; +} + +internal +RNTuple_Info *get_rntuple_info(Arena *arena, const char *fname, const char *ntpl_name) +{ + RNTuple_Info *info = arena_push_zeroed(arena); + + // Open the TFile + TFile *tfile = TFile::Open(fname, "READ"); + if (!tfile) { + fprintf(stderr, "Failed to open TFile.\n"); + return info; + } + defer { delete tfile; }; + + // Get the RNTuple information + const RNTuple *anchor = tfile->Get(ntpl_name); + if (anchor) { + info->anchor = *anchor; + info->header.start = anchor->GetSeekHeader(); + info->header.len = anchor->GetNBytesHeader(); + info->footer.start = anchor->GetSeekFooter(); + info->footer.len = anchor->GetNBytesFooter(); + } else { + fprintf(stderr, "RNTuple '%s' not found in %s.\n", ntpl_name, fname); + } + + return info; +} diff --git a/src/rntuple.h b/src/rntuple.h new file mode 100644 index 0000000..0c96416 --- /dev/null +++ b/src/rntuple.h @@ -0,0 +1,13 @@ +using RNTuple = ROOT::Experimental::RNTuple; + +struct Byte_Range { + u64 start, len; + u64 end() const { return start + len; } +}; + +struct RNTuple_Info { + RNTuple anchor; + + Byte_Range header; + Byte_Range footer; +}; diff --git a/src/rntviewer.cpp b/src/rntviewer.cpp index b7f77e2..5d7f75c 100644 --- a/src/rntviewer.cpp +++ b/src/rntviewer.cpp @@ -6,12 +6,6 @@ #include #include -// @Platform -#include -#include -#include -#include // for NAME_MAX - #ifdef DEBUG #include #endif @@ -27,11 +21,19 @@ #include "types.h" #include "defer.hpp" #include "mem.h" +#include "str.h" +#include "rntuple.h" +#include "window.h" #include "app_state.h" +// @Platform +#include "platform_linux.h" + namespace chr = std::chrono; #include "mem.cpp" +#include "str.cpp" +#include "rntuple.cpp" #include "render.cpp" #include "mainloop.cpp" @@ -86,16 +88,25 @@ GLFWwindow* init_glfw(i32 desired_win_width, i32 desired_win_height) return window; } +internal +void app_cleanup(App_State &app) +{ + os_stop_file_watch(app); + os_unmap_file(app.inspected_fmem, app.inspected_file_size); + if (app.inspected_file) fclose(app.inspected_file); + app.inspected_file = nullptr; +} + int main(int argc, char **argv) { - if (argc < 3) { + if (argc > 1 && argv[1][0] == '-') { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } // Collect arguments - const char *ntpl_name = argv[1]; - const char *fname = argv[2]; + const char *ntpl_name = argc > 1 ? argv[1] : nullptr; + const char *fname = argc > 2 ? argv[2] : nullptr; // Allocate program memory Arena *arena = arena_alloc(); @@ -118,57 +129,22 @@ int main(int argc, char **argv) return 1; } - // Open and map te file - FILE *file = fopen(fname, "rb"); - defer { fclose(file); }; - - int fd = fileno(file); - size_t fsize = file_size(file); - // @Platform - void *fmem = mmap(0, fsize, PROT_READ, MAP_SHARED_VALIDATE, fd, 0); - if (!fmem) { - fprintf(stderr, "Failed to open file %s\n", fname); - return 1; - } - defer { munmap(fmem, fsize); }; - - // @Platform: watch file for changes (to adapt the displayed file size - otherwise - // we may try to access invalid memory when the file gets shrunk) - int inot = inotify_init1(IN_NONBLOCK); - if (inot == -1) { - fprintf(stderr, "Failed to init inotify: %s (%d)\n", strerror(errno), errno); - return 1; - } - if (inotify_add_watch(inot, fname, IN_MODIFY) == -1) { - fprintf(stderr, "Failed to add inotify watch: %s (%d)\n", strerror(errno), errno); - return 1; - } - defer { close(inot); }; - - // Open the TFile -#if 0 - TFile *tfile = TFile::Open(fname, "READ"); - if (!tfile) { - fprintf(stderr, "Failed to open TFile.\n"); - return 1; - } - defer { delete tfile; }; - - // Get the RNTuple information - const RNTuple *anchor = tfile->Get(ntpl_name); - if (!anchor) { - fprintf(stderr, "RNTuple '%s' not found in %s.\n", ntpl_name, fname); - return 1; - } -#endif - - // Start main loop App_State app {}; - app.inspected_file = file; - app.inspected_fmem = (u8*)fmem; - app.inspected_file_size = fsize; - app.inot = inot; + defer { app_cleanup(app); }; + + // Open and map the file + if (fname) { + os_open_and_map_file(fname, app); + + // Watch file for changes (to adapt the displayed file size - otherwise + // we may try to access invalid memory when the file gets shrunk) + os_start_file_watch(fname, app); + } + + app.ntpl_name = ntpl_name; + app.rntinfo = get_rntuple_info(arena, fname, ntpl_name); + // Start main loop run_main_loop(window, arena, app); return 0; diff --git a/src/str.cpp b/src/str.cpp new file mode 100644 index 0000000..b4f06f3 --- /dev/null +++ b/src/str.cpp @@ -0,0 +1,22 @@ +internal +String8 push_str8fv(Arena *arena, const char *fmt, va_list args) +{ + va_list args2; + va_copy(args2, args); + u32 needed_bytes = vsnprintf(0, 0, fmt, args) + 1; + String8 result = {}; + result.str = arena_push_array_no_zero(arena, needed_bytes); + result.size = vsnprintf((char*)result.str, needed_bytes, fmt, args2); + result.str[result.size] = 0; + va_end(args2); + return result; +} + +String8 push_str8f(Arena *arena, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + String8 result = push_str8fv(arena, fmt, args); + va_end(args); + return result; +} diff --git a/src/str.h b/src/str.h new file mode 100644 index 0000000..7246132 --- /dev/null +++ b/src/str.h @@ -0,0 +1,12 @@ +struct String8 { + u8* str; + u64 size; + + operator const char *() const { + return (const char *)str; + } +}; + +#define str8(s) String8((u8*)(s), sizeof(s) - 1) + +String8 push_str8f(Arena *arena, char *fmt, ...); diff --git a/src/window.h b/src/window.h new file mode 100644 index 0000000..aff4a62 --- /dev/null +++ b/src/window.h @@ -0,0 +1,47 @@ +enum Input_Key { + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_Q, + KEY_ALT, + KEY_TAB, + + KEY_COUNT +}; + +enum Mouse_Button { + MOUSE_BTN_LEFT, + MOUSE_BTN_RIGHT, + + MOUSE_BTN_COUNT +}; + +enum Mouse_Button_State : u16 { + MOUSE_BTN_STATE_IS_DOWN = 0x1, + MOUSE_BTN_STATE_JUST_PRESSED = 0x2, + MOUSE_BTN_STATE_JUST_RELEASED = 0x4, +}; + +enum Key_State : u16 { + KEY_STATE_IS_DOWN = 0x1, + KEY_STATE_JUST_PRESSED = 0x2, + KEY_STATE_JUST_RELEASED = 0x4, +}; + +struct User_Input { + u16 key_state[KEY_COUNT]; + + u16 mouse_btn_state[MOUSE_BTN_COUNT]; + struct { i32 x; i32 y; } mouse_pos; +}; + +struct Window_Data { + // Real width and height of the window + i32 width; + i32 height; + bool size_just_changed; + + f32 desired_aspect_ratio; +}; + diff --git a/third_party/imgui_club/imgui_memory_editor.h b/third_party/imgui_club/imgui_memory_editor.h index 3a79aa9..50022e4 100644 --- a/third_party/imgui_club/imgui_memory_editor.h +++ b/third_party/imgui_club/imgui_memory_editor.h @@ -94,6 +94,8 @@ struct MemoryEditor ImU8 (*ReadFn)(const ImU8* data, size_t off); // = 0 // optional handler to read bytes. void (*WriteFn)(ImU8* data, size_t off, ImU8 d); // = 0 // optional handler to write bytes. bool (*HighlightFn)(const ImU8* data, size_t off);//= 0 // optional handler to return Highlight property (to support non-contiguous highlighting). + ImU32 (*BgColorFn)(const ImU8* data, size_t off, const void *UserData); // = 0 // optional handler to return custom background color of individual bytes. + const void *BgColorFnUserData; // [Internal State] bool ContentsWidthChanged; @@ -126,6 +128,8 @@ struct MemoryEditor ReadFn = NULL; WriteFn = NULL; HighlightFn = NULL; + BgColorFn = NULL; + BgColorFnUserData = NULL; // State/Internals ContentsWidthChanged = false; @@ -302,6 +306,19 @@ struct MemoryEditor } draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor); } + else if (BgColorFn) + { + ImVec2 pos = ImGui::GetCursorScreenPos(); + float highlight_width = s.GlyphWidth * 2; + bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, BgColorFnUserData) & IM_COL32_A_MASK) != 0); + if (is_next_byte_highlighted || (n + 1 == Cols)) + { + highlight_width = s.HexCellWidth; + if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) + highlight_width += s.SpacingBetweenMidCols; + } + draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), BgColorFn(mem_data, addr, BgColorFnUserData)); + } if (DataEditingAddr == addr) { @@ -413,6 +430,10 @@ struct MemoryEditor draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); } + else if (BgColorFn) + { + draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, BgColorFnUserData)); + } unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]; char display_c = (c < 32 || c >= 128) ? '.' : c; draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);