From 2c7d15de59ea5eb039da0e746d79430077e22be8 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Thu, 13 Jun 2024 11:28:35 -0700 Subject: [PATCH] pull out 'rich unwind' gathering path, which takes a 'concrete unwind' and produces a full unwind with debug-info-derived inline frames --- project.4coder | 4 +- src/df/core/df_core.c | 60 ++++++++++++++++- src/df/core/df_core.h | 42 ++++++++++++ src/df/core/df_core.mdesk | 1 + src/df/core/generated/df_core.meta.c | 3 +- src/df/core/generated/df_core.meta.h | 1 + src/df/gfx/df_views.c | 99 +++++++--------------------- 7 files changed, 131 insertions(+), 79 deletions(-) diff --git a/project.4coder b/project.4coder index 5409cda1..d64a1538 100644 --- a/project.4coder +++ b/project.4coder @@ -47,8 +47,8 @@ commands = { .rjf_f1 = { - .win = "build rdi_from_pdb rdi_dump && pushd build && rdi_from_pdb --pdb:mule_main.pdb --out:mule_main.rdi && rdi_dump mule_main.rdi > mule_main.dump && popd", - // .win = "build raddbg telemetry", + //.win = "build rdi_from_pdb rdi_dump && pushd build && rdi_from_pdb --pdb:mule_main.pdb --out:mule_main.rdi && rdi_dump mule_main.rdi > mule_main.dump && popd", + .win = "build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, diff --git a/src/df/core/df_core.c b/src/df/core/df_core.c index cf59414c..39912eb5 100644 --- a/src/df/core/df_core.c +++ b/src/df/core/df_core.c @@ -3654,6 +3654,58 @@ df_module_from_thread_candidates(DF_Entity *thread, DF_EntityList *candidates) return module; } +internal DF_Unwind +df_unwind_from_ctrl_unwind(Arena *arena, DI_Scope *di_scope, DF_Entity *process, CTRL_Unwind *base_unwind) +{ + Temp scratch = scratch_begin(&arena, 1); + DF_UnwindFrameList rich_frames_list = {0}; + Architecture arch = df_architecture_from_entity(process); + for(U64 base_frame_idx = 0; base_frame_idx < base_unwind->frames.count; base_frame_idx += 1) + { + CTRL_UnwindFrame *base_frame = &base_unwind->frames.v[base_frame_idx]; + U64 rip_vaddr = regs_rip_from_arch_block(arch, base_frame->regs); + DF_Entity *module = df_module_from_process_vaddr(process, rip_vaddr); + U64 rip_voff = df_voff_from_vaddr(module, rip_vaddr); + DI_Key dbgi_key = df_dbgi_key_from_module(module); + RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0); + RDI_Scope *scope = rdi_scope_from_voff(rdi, rip_voff); + + // rjf: add rich frames for inlines + for(RDI_Scope *s = scope; s->inline_site_idx != 0; s = rdi_element_from_name_idx(rdi, Scopes, s->parent_scope_idx)) + { + RDI_InlineSite *site = rdi_element_from_name_idx(rdi, InlineSites, s->inline_site_idx); + DF_UnwindFrameNode *n = push_array(scratch.arena, DF_UnwindFrameNode, 1); + SLLQueuePush(rich_frames_list.first, rich_frames_list.last, n); + rich_frames_list.count += 1; + n->v.regs = base_frame->regs; + n->v.rdi = rdi; + n->v.procedure = 0; + n->v.inline_site = site; + } + + // rjf: add frame for concrete frame + DF_UnwindFrameNode *n = push_array(scratch.arena, DF_UnwindFrameNode, 1); + SLLQueuePush(rich_frames_list.first, rich_frames_list.last, n); + rich_frames_list.count += 1; + n->v.regs = base_frame->regs; + n->v.rdi = rdi; + n->v.procedure = rdi_element_from_name_idx(rdi, Procedures, scope->proc_idx); + n->v.inline_site = 0; + } + DF_Unwind result = {0}; + { + result.frames.count = rich_frames_list.count; + result.frames.v = push_array(arena, DF_UnwindFrame, result.frames.count); + U64 idx = 0; + for(DF_UnwindFrameNode *n = rich_frames_list.first; n != 0; n = n->next, idx += 1) + { + MemoryCopyStruct(&result.frames.v[idx], &n->v); + } + } + scratch_end(scratch); + return result; +} + //////////////////////////////// //~ rjf: Entity -> Log Entities @@ -5652,6 +5704,7 @@ df_ctrl_ctx_apply_overrides(DF_CtrlCtx *ctx, DF_CtrlCtx *overrides) { ctx->thread = overrides->thread; ctx->unwind_count = overrides->unwind_count; + ctx->inline_unwind_count = overrides->inline_unwind_count; } } @@ -7565,8 +7618,8 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) //- rjf: debug control context management operations case DF_CoreCmdKind_SelectThread: { + MemoryZeroStruct(&df_state->ctrl_ctx); df_state->ctrl_ctx.thread = params.entity; - df_state->ctrl_ctx.unwind_count = 0; }break; case DF_CoreCmdKind_SelectUnwind: { @@ -7575,6 +7628,11 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) U64 max_unwind = unwind.frames.count ? unwind.frames.count-1 : 0; U64 index = Clamp(0, params.index, max_unwind); df_state->ctrl_ctx.unwind_count = index; + df_state->ctrl_ctx.inline_unwind_count = 0; + }break; + case DF_CoreCmdKind_SelectInlineUnwind: + { + }break; case DF_CoreCmdKind_UpOneFrame: { diff --git a/src/df/core/df_core.h b/src/df/core/df_core.h index a8a72850..824cfc38 100644 --- a/src/df/core/df_core.h +++ b/src/df/core/df_core.h @@ -77,6 +77,7 @@ struct DF_CtrlCtx { DF_Handle thread; U64 unwind_count; + U64 inline_unwind_count; }; //////////////////////////////// @@ -516,6 +517,46 @@ struct DF_EntityFuzzyItemArray U64 count; }; +//////////////////////////////// +//~ rjf: Rich (Including Inline) Unwind Types + +typedef struct DF_UnwindFrame DF_UnwindFrame; +struct DF_UnwindFrame +{ + void *regs; + RDI_Parsed *rdi; + RDI_Procedure *procedure; + RDI_InlineSite *inline_site; +}; + +typedef struct DF_UnwindFrameNode DF_UnwindFrameNode; +struct DF_UnwindFrameNode +{ + DF_UnwindFrameNode *next; + DF_UnwindFrame v; +}; + +typedef struct DF_UnwindFrameList DF_UnwindFrameList; +struct DF_UnwindFrameList +{ + DF_UnwindFrameNode *first; + DF_UnwindFrameNode *last; + U64 count; +}; + +typedef struct DF_UnwindFrameArray DF_UnwindFrameArray; +struct DF_UnwindFrameArray +{ + DF_UnwindFrame *v; + U64 count; +}; + +typedef struct DF_Unwind DF_Unwind; +struct DF_Unwind +{ + DF_UnwindFrameArray frames; +}; + //////////////////////////////// //~ rjf: Source <-> Disasm Types @@ -1562,6 +1603,7 @@ internal EVAL_String2NumMap *df_push_locals_map_from_dbgi_key_voff(Arena *arena, internal EVAL_String2NumMap *df_push_member_map_from_dbgi_key_voff(Arena *arena, DI_Scope *scope, DI_Key *dbgi_key, U64 voff); internal B32 df_set_thread_rip(DF_Entity *thread, U64 vaddr); internal DF_Entity *df_module_from_thread_candidates(DF_Entity *thread, DF_EntityList *candidates); +internal DF_Unwind df_unwind_from_ctrl_unwind(Arena *arena, DI_Scope *di_scope, DF_Entity *process, CTRL_Unwind *base_unwind); //////////////////////////////// //~ rjf: Entity -> Log Entities diff --git a/src/df/core/df_core.mdesk b/src/df/core/df_core.mdesk index 3297cce4..f4c5b70d 100644 --- a/src/df/core/df_core.mdesk +++ b/src/df/core/df_core.mdesk @@ -152,6 +152,7 @@ DF_CoreCmdTable:// | | | {SelectThreadWindow 0 Entity Thread 0 0 0 0 0 1 Null "select_thread_window" "Select Thread On Window" "Selects a thread for the active window, overriding the global selected thread." "" } {SelectThreadView 0 Entity Thread 0 0 0 0 0 1 Null "select_thread_view" "Select Thread On View" "Selects a thread for the active view, overriding the global and per-window selected threads." "" } {SelectUnwind 1 Null Nil 0 0 0 0 0 0 Null "select_unwind" "Select Unwind" "Selects an unwind frame number for the selected thread." "" } + {SelectInlineUnwind 1 Null Nil 0 0 0 0 0 0 Null "select_inline_unwind" "Select Inline Unwind" "Selects an inline unwind frame number for the selected thread." "" } {UpOneFrame 0 Null Nil 0 0 0 0 0 0 UpArrow "up_one_frame" "Up One Frame" "Selects the callstack frame above the currently selected." "" } {DownOneFrame 0 Null Nil 0 0 0 0 0 0 DownArrow "down_one_frame" "Down One Frame" "Selects the callstack frame below the currently selected." "" } {FreezeThread 0 Entity Thread 0 0 0 0 0 1 Locked "freeze_thread" "Freeze Thread" "Freezes the passed thread." "" } diff --git a/src/df/core/generated/df_core.meta.c b/src/df/core/generated/df_core.meta.c index 88fc1a8d..55634ee9 100644 --- a/src/df/core/generated/df_core.meta.c +++ b/src/df/core/generated/df_core.meta.c @@ -212,7 +212,7 @@ DF_CoreCmdKind_Null, DF_CoreCmdKind_Null, }; -DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[221] = +DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[222] = { { str8_lit_comp(""), str8_lit_comp(""), str8_lit_comp(""), str8_lit_comp(""), (DF_CmdSpecFlag_OmitFromLists*1), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_Null}, { str8_lit_comp("exit"), str8_lit_comp("Exits the debugger."), str8_lit_comp("quit,close,abort"), str8_lit_comp("Exit"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_X}, @@ -247,6 +247,7 @@ DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[221] = { str8_lit_comp("select_thread_window"), str8_lit_comp("Selects a thread for the active window, overriding the global selected thread."), str8_lit_comp(""), str8_lit_comp("Select Thread On Window"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Entity, DF_EntityKind_Thread, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*1)}, DF_IconKind_Null}, { str8_lit_comp("select_thread_view"), str8_lit_comp("Selects a thread for the active view, overriding the global and per-window selected threads."), str8_lit_comp(""), str8_lit_comp("Select Thread On View"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Entity, DF_EntityKind_Thread, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*1)}, DF_IconKind_Null}, { str8_lit_comp("select_unwind"), str8_lit_comp("Selects an unwind frame number for the selected thread."), str8_lit_comp(""), str8_lit_comp("Select Unwind"), (DF_CmdSpecFlag_OmitFromLists*1), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_Null}, +{ str8_lit_comp("select_inline_unwind"), str8_lit_comp("Selects an inline unwind frame number for the selected thread."), str8_lit_comp(""), str8_lit_comp("Select Inline Unwind"), (DF_CmdSpecFlag_OmitFromLists*1), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_Null}, { str8_lit_comp("up_one_frame"), str8_lit_comp("Selects the callstack frame above the currently selected."), str8_lit_comp(""), str8_lit_comp("Up One Frame"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_UpArrow}, { str8_lit_comp("down_one_frame"), str8_lit_comp("Selects the callstack frame below the currently selected."), str8_lit_comp(""), str8_lit_comp("Down One Frame"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_DownArrow}, { str8_lit_comp("freeze_thread"), str8_lit_comp("Freezes the passed thread."), str8_lit_comp(""), str8_lit_comp("Freeze Thread"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Entity, DF_EntityKind_Thread, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*1)}, DF_IconKind_Locked}, diff --git a/src/df/core/generated/df_core.meta.h b/src/df/core/generated/df_core.meta.h index c840bb48..fe27d1a6 100644 --- a/src/df/core/generated/df_core.meta.h +++ b/src/df/core/generated/df_core.meta.h @@ -81,6 +81,7 @@ DF_CoreCmdKind_SelectThread, DF_CoreCmdKind_SelectThreadWindow, DF_CoreCmdKind_SelectThreadView, DF_CoreCmdKind_SelectUnwind, +DF_CoreCmdKind_SelectInlineUnwind, DF_CoreCmdKind_UpOneFrame, DF_CoreCmdKind_DownOneFrame, DF_CoreCmdKind_FreezeThread, diff --git a/src/df/gfx/df_views.c b/src/df/gfx/df_views.c index 971c999a..9d65df49 100644 --- a/src/df/gfx/df_views.c +++ b/src/df/gfx/df_views.c @@ -4850,74 +4850,8 @@ DF_VIEW_UI_FUNCTION_DEF(CallStack) { thread_color = df_rgba_from_entity(thread); } - CTRL_Unwind unwind = df_query_cached_unwind_from_thread(thread); - - //- rjf: compute full call stack, including concrete & inline frames - typedef struct DF_CallStackFrame DF_CallStackFrame; - struct DF_CallStackFrame - { - void *regs; - B32 is_inline_frame; - String8 name; - String8 type_string; - }; - typedef struct DF_CallStackFrameNode DF_CallStackFrameNode; - struct DF_CallStackFrameNode - { - DF_CallStackFrameNode *next; - DF_CallStackFrame v; - }; - DF_CallStackFrame *frames = 0; - U64 frames_count = 0; - { - TG_Graph *graph = tg_graph_begin(bit_size_from_arch(arch)/8, 256); - DF_CallStackFrameNode *first_frame = 0; - DF_CallStackFrameNode *last_frame = 0; - for(U64 unwind_idx = 0; unwind_idx < unwind.frames.count; unwind_idx += 1) - { - CTRL_UnwindFrame *unwind_f = &unwind.frames.v[unwind_idx]; - U64 rip_vaddr = regs_rip_from_arch_block(arch, unwind_f->regs); - DF_Entity *module = df_module_from_process_vaddr(process, rip_vaddr); - U64 rip_voff = df_voff_from_vaddr(module, rip_vaddr); - DI_Key dbgi_key = df_dbgi_key_from_module(module); - RDI_Parsed *rdi = di_rdi_from_key(scope, &dbgi_key, 0); - RDI_Scope *scope = rdi_scope_from_voff(rdi, rip_voff); - RDI_Procedure *procedure = rdi_procedure_from_scope(rdi, scope); - RDI_TypeNode *procedure_type = rdi_element_from_name_idx(rdi, TypeNodes, procedure->type_idx); - - // rjf: add frame for inlines - for(RDI_Scope *s = scope; s->inline_site_idx != 0; s = rdi_element_from_name_idx(rdi, Scopes, s->parent_scope_idx)) - { - RDI_InlineSite *site = rdi_element_from_name_idx(rdi, InlineSites, s->inline_site_idx); - RDI_TypeNode *site_type = rdi_element_from_name_idx(rdi, TypeNodes, site->type_idx); - DF_CallStackFrameNode *n = push_array(scratch.arena, DF_CallStackFrameNode, 1); - SLLQueuePush(first_frame, last_frame, n); - n->v.regs = unwind_f->regs; - n->v.is_inline_frame = 1; - n->v.name.str = rdi_string_from_idx(rdi, site->name_string_idx, &n->v.name.size); - n->v.type_string = tg_string_from_key(scratch.arena, graph, rdi, tg_key_ext(tg_kind_from_rdi_type_kind(site_type->kind), (U64)site->type_idx)); - frames_count += 1; - } - - // rjf: add frame for concrete frame - DF_CallStackFrameNode *n = push_array(scratch.arena, DF_CallStackFrameNode, 1); - SLLQueuePush(first_frame, last_frame, n); - n->v.regs = unwind_f->regs; - n->v.is_inline_frame = 0; - n->v.name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &n->v.name.size); - n->v.type_string = tg_string_from_key(scratch.arena, graph, rdi, tg_key_ext(tg_kind_from_rdi_type_kind(procedure_type->kind), (U64)procedure->type_idx)); - frames_count += 1; - } - frames = push_array(scratch.arena, DF_CallStackFrame, frames_count); - { - U64 idx = 0; - for(DF_CallStackFrameNode *f = first_frame; f != 0; f = f->next) - { - MemoryCopyStruct(&frames[idx], &f->v); - idx += 1; - } - } - } + CTRL_Unwind base_unwind = df_query_cached_unwind_from_thread(thread); + DF_Unwind rich_unwind = df_unwind_from_ctrl_unwind(scratch.arena, scope, process, &base_unwind); //- rjf: grab state typedef struct DF_CallStackViewState DF_CallStackViewState; @@ -4948,8 +4882,8 @@ DF_VIEW_UI_FUNCTION_DEF(CallStack) scroll_list_params.flags = UI_ScrollListFlag_All; scroll_list_params.row_height_px = floor_f32(ui_top_font_size()*2.5f); scroll_list_params.dim_px = dim_2f32(rect); - scroll_list_params.cursor_range = r2s64(v2s64(0, 0), v2s64(3, frames_count)); - scroll_list_params.item_range = r1s64(0, frames_count+1); + scroll_list_params.cursor_range = r2s64(v2s64(0, 0), v2s64(3, rich_unwind.frames.count)); + scroll_list_params.item_range = r1s64(0, rich_unwind.frames.count+1); scroll_list_params.cursor_min_is_empty_selection[Axis2_Y] = 1; } UI_ScrollListSignal scroll_list_sig = {0}; @@ -4982,7 +4916,9 @@ DF_VIEW_UI_FUNCTION_DEF(CallStack) } //- rjf: frame rows - for(S64 row_num = visible_row_range.min; row_num <= visible_row_range.max && row_num <= frames_count; row_num += 1) + for(S64 row_num = visible_row_range.min; + row_num <= visible_row_range.max && row_num <= rich_unwind.frames.count; + row_num += 1) { if(row_num == 0) { @@ -4992,12 +4928,25 @@ DF_VIEW_UI_FUNCTION_DEF(CallStack) // rjf: unpack frame U64 frame_idx = row_num-1; - DF_CallStackFrame *frame = &frames[frame_idx]; + DF_UnwindFrame *frame = &rich_unwind.frames.v[frame_idx]; U64 rip_vaddr = regs_rip_from_arch_block(thread->arch, frame->regs); DF_Entity *module = df_module_from_process_vaddr(process, rip_vaddr); B32 frame_valid = (rip_vaddr != 0); - String8 symbol_name = frame->name; - String8 symbol_type_string = frame->type_string; + TG_Graph *graph = tg_graph_begin(bit_size_from_arch(thread->arch)/8, 256); + String8 symbol_name = {0}; + String8 symbol_type_string = {0}; + if(frame->procedure != 0) + { + symbol_name.str = rdi_name_from_procedure(frame->rdi, frame->procedure, &symbol_name.size); + RDI_TypeNode *type = rdi_element_from_name_idx(frame->rdi, TypeNodes, frame->procedure->type_idx); + symbol_type_string = tg_string_from_key(scratch.arena, graph, frame->rdi, tg_key_ext(tg_kind_from_rdi_type_kind(type->kind), frame->procedure->type_idx)); + } + if(frame->inline_site != 0) + { + symbol_name.str = rdi_string_from_idx(frame->rdi, frame->inline_site->name_string_idx, &symbol_name.size); + RDI_TypeNode *type = rdi_element_from_name_idx(frame->rdi, TypeNodes, frame->inline_site->type_idx); + symbol_type_string = tg_string_from_key(scratch.arena, graph, frame->rdi, tg_key_ext(tg_kind_from_rdi_type_kind(type->kind), frame->inline_site->type_idx)); + } // rjf: build row if(frame_valid) UI_NamedTableVectorF("###callstack_%p_%I64x", view, frame_idx) @@ -5058,7 +5007,7 @@ DF_VIEW_UI_FUNCTION_DEF(CallStack) UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clickable|UI_BoxFlag_Clip, "frame_%I64x", frame_idx); UI_Parent(box) { - if(frame->is_inline_frame) + if(frame->inline_site != 0) { UI_PrefWidth(ui_text_dim(10, 1)) {