From 0a10e855a5ee1e793d7677c2cd142c59db1c9c41 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Tue, 14 Apr 2026 19:52:10 -0700 Subject: [PATCH] memory view fixes, changelog, notes --- CHANGELOG.md | 18 +++++++++++++ src/base/base_arena.c | 17 ++++++------ src/raddbg/generated/raddbg.meta.c | 2 +- src/raddbg/raddbg.mdesk | 2 +- src/raddbg/raddbg_main.c | 43 ++++++++++++++++++------------ src/raddbg/raddbg_views.c | 11 ++++---- 6 files changed, 61 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb74ef70..0fba8b26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## Debugger Changes +- Added a dedicated cursor address bar to the memory view. This can be focused + with the keyboard with the `Focus Menu` command. By default, this command is + bound to `Alt + D`, but if you have existing configuration, you may need to + bind it yourself manually (this can be done in the palette). This address bar + accepts any expression, and controls the cursor's address. All other + configuration options are still available in the memory view settings. +- Added "Peek Types" to the memory view. These control the set of types which + are used to interpret bytes at the cursor. Options exist for basic cases, + like integers and floats, but the view also supports entering a list of + full type expressions, which can include user-defined types like structures. +- Added annotations for members of structure variables to the memory view. +- Added a dedicated zoom setting for the memory view. +- Added a setting for automatically determining the number of columns in the + memory view based on the available space. +- Fixed many bugs and improved visuals in the memory view. - Fixed the debugger incorrectly evaluating pointer casts of registers in register space, rather than promoting them to process address space evaluations. This fixes cases where expressions like `(int *)rax` were not @@ -19,6 +34,9 @@ - Fixed the debugger not showing full call stacks when the top instruction pointer is located at a 0 address. - Fixed several crashes and instabilities. +- Added a dedicated settings button to the focused tab. This opens the same + menu as right-clicking the tab, but this makes this menu (and thus all + options for all tabs and views) more discoverable. - Added optional cursor trails, to visualize cursor motion. Can be turned off by toggling the `Cursor Trail` setting. - Added project names. When set, this name is used for displaying the project diff --git a/src/base/base_arena.c b/src/base/base_arena.c index 5ed1ca64..c9e76289 100644 --- a/src/base/base_arena.c +++ b/src/base/base_arena.c @@ -11,7 +11,7 @@ arena_alloc_(ArenaParams *params) { U64 reserve_size = params->reserve_size; U64 commit_size = params->commit_size; - + // rjf: reserve/commit initial block void *base = params->optional_backing_buffer; if(base == 0) @@ -27,7 +27,7 @@ arena_alloc_(ArenaParams *params) reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size); commit_size = AlignPow2(commit_size, os_get_system_info()->page_size); } - + if(params->flags & ArenaFlag_LargePages) { base = os_reserve_large(reserve_size); @@ -39,7 +39,8 @@ arena_alloc_(ArenaParams *params) os_commit(base, commit_size); } AsanPoisonMemoryRegion(base, commit_size); - raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line); + // TODO(rjf): we need to reintroduce this later when we have the ability to remove annotations... + // raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line); } else { @@ -85,7 +86,7 @@ arena_release(Arena *arena) tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, 0, "%s/%p", base_arena->name, base_arena); } #endif - + for(Arena *n = arena->current, *prev = 0; n != 0; n = prev) { prev = n->prev; @@ -152,7 +153,7 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero) .flags = current->flags, .allocation_site_file = current->allocation_site_file, .allocation_site_line = current->allocation_site_line); - + size_to_zero = 0; } else @@ -197,7 +198,7 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero) AsanUnpoisonMemoryRegion(result, size); MemoryZero(result, size_to_zero); } - + #if PROFILE_TELEMETRY if(size > KB(1)) { @@ -253,7 +254,7 @@ arena_pop_to(Arena *arena, U64 pos) AssertAlways(new_pos <= current->pos); AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos)); current->pos = new_pos; - + #if PROFILE_TELEMETRY if((pos - (new_pos + current->base_pos)) > KB(1)) { @@ -262,7 +263,7 @@ arena_pop_to(Arena *arena, U64 pos) tmPlot(0, TM_PLOT_UNITS_MEMORY, TM_PLOT_DRAW_LINE, (double)(current->base_pos + current->pos) / 1024.0 / 1024.0, "%s/%p", base_arena->name, base_arena); } #endif - + } //- rjf: arena push/pop helpers diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index 4484749e..8101e7fc 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -437,7 +437,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[26] = {str8_lit_comp("list"), str8_lit_comp("x:\n{\n @description(\"An expression describing the first node in the list.\")\n 'expression': expr_string,\n @order(0) @description(\"The name of the member which encodes the link to the next node.\")\n 'member_name': code_string,\n}\n")}, {str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(1) @display_name('Line Wrapping') @description(\"Splits textual lines into multiple visual lines, so that all text is within the visible area.\")\n 'line_wrapping': bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change': bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, {str8_lit_comp("disasm"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n 'arch': code_string,\n 'syntax': code_string,\n 'size': expr_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n}\n")}, -{str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Zoom\") @description(\"The zoom level for displaying bytes in the memory view.\")\n @default(1) 'zoom': @range[0.25, 2] f32,\n @display_name(\"Base Address\") @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @default(1) @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @expand_if(\"!$.auto_columns\") @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(16) @display_name(\"Default Radix\") @description(\"The default radix with which numeric values should be displayed, when peeking.\")\n @or(2, 8, 10, 16)\n 'default_radix': u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n @default(1) @display_name(\"Allow Mutation\") @description(\"Allows operations which mutate memory.\")\n 'allow_mutation': bool,\n @default(0) @display_name(\"Automatically Size Columns\") @description(\"Determines the number of columns based on the available space.\")\n 'auto_columns': bool,\n @default(1) @display_name(\"Peek As Unsigned\") 'peek_as_unsigned': bool,\n @default(1) @display_name(\"Peek As Signed\") 'peek_as_signed': bool,\n @default(1) @display_name(\"Peek As Float\") 'peek_as_float': bool,\n @display_name(\"Extra Peek Types\") @description(\"A list of types as which to interpret selected memory.\")\n 'peek_types': query,\n}\n")}, +{str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Zoom\") @description(\"The zoom level for displaying bytes in the memory view.\")\n @default(1.0) 'zoom': @range[0.5, 2] f32,\n @display_name(\"Base Address\") @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @default(1) @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @expand_if(\"!$.auto_columns\") @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(16) @display_name(\"Default Radix\") @description(\"The default radix with which numeric values should be displayed, when peeking.\")\n @or(2, 8, 10, 16)\n 'default_radix': u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n @default(1) @display_name(\"Allow Mutation\") @description(\"Allows operations which mutate memory.\")\n 'allow_mutation': bool,\n @default(0) @display_name(\"Automatically Size Columns\") @description(\"Determines the number of columns based on the available space.\")\n 'auto_columns': bool,\n @default(1) @display_name(\"Peek As Unsigned\") 'peek_as_unsigned': bool,\n @default(1) @display_name(\"Peek As Signed\") 'peek_as_signed': bool,\n @default(1) @display_name(\"Peek As Float\") 'peek_as_float': bool,\n @display_name(\"Extra Peek Types\") @description(\"A list of types as which to interpret selected memory.\")\n 'peek_types': query,\n}\n")}, {str8_lit_comp("bitmap"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as a bitmap.\")\n 'expression': expr_string,\n @description(\"An expression describing the width of the bitmap, in pixels.\") @order(0) 'w': u64,\n @description(\"An expression describing the height of the bitmap, in pixels.\") @order(1) 'h': u64,\n @display_name(\"Bitmap Format\") @description(\"The pixel format that the bitmap data should be interpreted as being within.\")\n 'fmt': code_string,\n}\n")}, {str8_lit_comp("color"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Value\") @description(\"An expression to describe the value or location of the color.\")\n 'expression': expr_string,\n}\n")}, {str8_lit_comp("geo3d"), str8_lit_comp("@inherit(tab) x:\n{\n @display_name(\"Expression\") @description(\"An expression to describe the base address of the index buffer.\")\n 'expression': expr_string,\n 'count': expr_string,\n 'vtx': expr_string,\n 'vtx_size': expr_string,\n 'yaw': @range[0, 1] f32,\n 'pitch': @range[-0.5, 0] f32,\n 'zoom': @range[0, 100] f32,\n}\n")}, diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index ad103e6c..a3fc863b 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -541,7 +541,7 @@ RD_VocabTable: @inherit(tab) x: { @display_name("Zoom") @description("The zoom level for displaying bytes in the memory view.") - @default(1) 'zoom': @range[0.25, 2] f32, + @default(1.0) 'zoom': @range[0.5, 2] f32, @display_name("Base Address") @description("An expression which refers to the base address of data which should be viewed as memory.") 'expression': expr_string, @display_name("Address Range Size") @description("The number of bytes of the viewed memory range.") diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 7a6599f6..28e3bb92 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -2,30 +2,21 @@ // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// -//~ rjf: post-0.9.20 TODO notes +//~ rjf: post-0.9.25 TODO notes +// +//- evaluation space coverage pass +// [ ] eval space reads/writes -> needs staleness/badness info - replace ctrl layer, to apply to all spaces +// [ ] need concrete ways of referring into a space at any offset - e.g. `process.memory + 0x1234`, `file:"foo".data + 0x1234`, `thread.regs + 0x80`, etc. +// [ ] memory view needs to take advantage of above when peeking; ensure peeking works on files etc. // //- memory view pass -// [ ] scroll bar -// [ ] horizontal scrolling? // [ ] toggleable ascii column -// [ ] toggleable hierarchy column? -// [x] cursor info at bottom? # of selected bytes, address range, cursor hierarchical location? -// [x] clean up tooltip? it's a bit big/noisy (maybe remove it after above?) -// [x] missing call stack frames? +// [ ] toggleable annotation tree column +// [ ] horizontal scrolling // //- fabian complaints / reports -// [ ] broadly, need to complete memory view & fix issues -// [ ] dumb panel click-through thing -// [ ] correct type interpretations; togglable interpretations -// [ ] make address editable; specialized version of the cursor address editor in tab right-click menu // [ ] simplified default layout // [ ] memory view deserves larger spot, in default layout -// [x] fix for-loop stepping oddities, likely single-line for-loop stepping -// [x] `foo, x` needs to correctly match `hex(foo)`, e.g. in application to expansions -// [x] eval: pointer casts of register space should promote to process space -// [x] more control over string visualization; specifically, when *not* to do it, even when using e.g. char *s -// [x] sign bits should not display on unsigned integers? -// [x] source/disasm ctx menu should include options for e.g. "go to selected thread" // [ ] ambiguous parsing cases - need to re-introduce identifier resolution into eval parse. (foo *)&bar // [ ] broadly, view discoverability / docs // [ ] signify empty watch window "expression" slot more as a text field? @@ -217,6 +208,24 @@ // [ ] font cache eviction (both for font tags, closing fp handles, and // rasterizations) +//////////////////////////////// +//~ rjf: Recently Completed +// +// [x] fix for-loop stepping oddities, likely single-line for-loop stepping +// [x] `foo, x` needs to correctly match `hex(foo)`, e.g. in application to expansions +// [x] eval: pointer casts of register space should promote to process space +// [x] more control over string visualization; specifically, when *not* to do it, even when using e.g. char *s +// [x] sign bits should not display on unsigned integers? +// [x] source/disasm ctx menu should include options for e.g. "go to selected thread" +// [x] correct type interpretations; togglable interpretations +// [x] make address editable; specialized version of the cursor address editor in tab right-click menu +// [x] broadly, need to complete memory view & fix issues +// [x] dumb panel click-through thing +// [x] cursor info at bottom? # of selected bytes, address range, cursor hierarchical location? +// [x] clean up tooltip? it's a bit big/noisy (maybe remove it after above?) +// [x] missing call stack frames? +// [x] scroll bar + //////////////////////////////// //~ rjf: Build Options diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 9058fb07..00540cce 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -2691,7 +2691,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) FNT_RasterFlags font_raster_flags = rd_raster_flags_from_slot(RD_FontSlot_Code); F32 tab_font_size = ui_top_font_size(); F32 zoom_target = rd_view_setting_f32_from_name(str8_lit("zoom")); - zoom_target = Clamp(0.25f, zoom_target, 2.f); + zoom_target = Clamp(0.5f, zoom_target, 2.f); F32 zoom = ui_anim(ui_key_from_stringf(ui_key_zero(), "%I64x_zoom", rd_regs()->view), zoom_target, .initial = zoom_target); F32 cell_font_size = tab_font_size * zoom; F32 cell_big_glyph_advance = fnt_dim_from_tag_size_string(font, cell_font_size, 0, 0, str8_lit("H")).x; @@ -2724,6 +2724,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) F32 num_columns_f = ((dim_2f32(rect).x - address_margin_width_px - cell_big_glyph_advance*4.f) / (cell_width_px + cell_big_glyph_advance)); num_columns_f = ClampBot(1.f, num_columns_f); num_columns = (U64)num_columns_f; + num_columns = ClampTop(128, num_columns); } num_columns = ClampBot(1, num_columns); UI_ScrollPt2 scroll_pos = rd_view_scroll_pos(); @@ -2782,8 +2783,8 @@ RD_VIEW_UI_FUNCTION_DEF(memory) ////////////////////////////// //- rjf: determine visible range of rows (occluded & non-occluded) // - S64 num_possible_visible_rows = num_possible_visible_rows = dim_2f32(rect).y/row_height_px;; - S64 num_possible_nonoccluded_visible_rows = (dim_2f32(content_rect).y - dim_2f32(footer_rect).y) / row_height_px; + S64 num_possible_visible_rows = dim_2f32(rect).y/row_height_px; + S64 num_possible_nonoccluded_visible_rows = (dim_2f32(content_rect).y - dim_2f32(header_rect).y - dim_2f32(footer_rect).y) / row_height_px - 1; ////////////////////////////// //- rjf: determine legal scroll range @@ -2804,7 +2805,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) (mv->last_cursor_range.min != cursor_range.min || mv->last_cursor_range.max != cursor_range.max)) { - mv->contain_cursor = 1; + mv->center_cursor = 1; if(track_mark_to_cursor) { mark_base_vaddr = cursor_base_vaddr; @@ -3104,7 +3105,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) { mv->contain_cursor = 0; Rng1S64 viz_range_nonoccluded_rows = {0}; - viz_range_nonoccluded_rows.min = scroll_pos.y.idx + (S64)(content_rect.y0 / row_height_px); + viz_range_nonoccluded_rows.min = scroll_pos.y.idx + (S64)((content_rect.y0 - header_rect.y0) / row_height_px); viz_range_nonoccluded_rows.max = viz_range_nonoccluded_rows.min + num_possible_nonoccluded_visible_rows; viz_range_nonoccluded_rows.min = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.min); viz_range_nonoccluded_rows.max = clamp_1s64(scroll_idx_rng, viz_range_nonoccluded_rows.max);