From e5d6a49055e8407760fed8e11f10a3849fb1fce3 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 14 May 2025 21:44:09 -0700 Subject: [PATCH] first pass at call stack cache, need a lot of clean up before this is working... --- src/ctrl/ctrl_core.c | 98 +++++++++++++++++++++++++++---------- src/ctrl/ctrl_core.h | 17 +++++-- src/raddbg/raddbg_core.c | 42 +++++++++------- src/raddbg/raddbg_core.h | 1 + src/raddbg/raddbg_eval.c | 10 ++-- src/raddbg/raddbg_views.c | 23 +++++---- src/raddbg/raddbg_widgets.c | 10 ++-- 7 files changed, 132 insertions(+), 69 deletions(-) diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index baeb2a43..e6a1a193 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -1443,7 +1443,7 @@ ctrl_scope_close(CTRL_Scope *scope) internal void ctrl_scope_touch_call_stack_node__stripe_r_guarded(CTRL_Scope *scope, CTRL_CallStackCacheNode *node) { - ins_atomic_u64_eval(&node->scope_touch_count); + ins_atomic_u64_inc_eval(&node->scope_touch_count); CTRL_ScopeCallStackTouch *touch = ctrl_tctx->free_call_stack_touch; if(touch != 0) { @@ -3299,13 +3299,21 @@ ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *bas } //- rjf: package - result.count = frame_count; - result.frames = push_array(arena, CTRL_CallStackFrame, result.count); + result.frames_count = frame_count; + result.frames = push_array(arena, CTRL_CallStackFrame, result.frames_count); + result.concrete_frames_count = base_unwind->frames.count; + result.concrete_frames = push_array(arena, CTRL_CallStackFrame *, result.concrete_frames_count); { U64 idx = 0; + U64 concrete_idx = 0; for(FrameNode *n = first_frame; n != 0; n = n->next, idx += 1) { MemoryCopyStruct(&result.frames[idx], &n->v); + if(n->v.inline_depth == 0 && concrete_idx < result.concrete_frames_count) + { + result.concrete_frames[concrete_idx] = &result.frames[idx]; + concrete_idx += 1; + } } } } @@ -3320,7 +3328,7 @@ ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U CTRL_CallStackFrame *f = 0; { U64 base_frame_idx = 0; - for(U64 idx = 0; idx < call_stack->count; idx += 1) + for(U64 idx = 0; idx < call_stack->frames_count; idx += 1) { if(call_stack->frames[idx].inline_depth == 0) { @@ -3361,7 +3369,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Entity *thread, U64 endt_us) //- rjf: loop: try to grab cached call stack; request; wait U64 reg_gen = ctrl_reg_gen(); U64 mem_gen = ctrl_mem_gen(); - OS_MutexScopeW(stripe->rw_mutex) for(;;) + OS_MutexScopeR(stripe->rw_mutex) for(;;) { //- rjf: try to grab cached B32 is_good = 0; @@ -3385,20 +3393,31 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Entity *thread, U64 endt_us) } //- rjf: create node if needed - if(!is_good) + if(!is_good) OS_MutexScopeRWPromote(stripe->rw_mutex) { - node = push_array(stripe->arena, CTRL_CallStackCacheNode, 1); - DLLPushBack(slot->first, slot->last, node); - node->thread = thread->handle; + for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) + { + if(ctrl_handle_match(n->thread, handle)) + { + node = n; + break; + } + } + if(node == 0) + { + node = push_array(stripe->arena, CTRL_CallStackCacheNode, 1); + DLLPushBack(slot->first, slot->last, node); + node->thread = thread->handle; + } } //- rjf: request if needed - if(!is_working && (!is_good || !is_stale)) + if(!is_working && (!is_good || is_stale)) { if(ctrl_u2csb_enqueue_req(thread->handle, endt_us) && async_push_work(ctrl_call_stack_build_work)) { - node->working_count += 1; + ins_atomic_u64_inc_eval(&node->working_count); } } @@ -3409,7 +3428,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Entity *thread, U64 endt_us) } //- rjf: time to wait for new result? -> wait - os_condition_variable_wait_rw_w(stripe->cv, stripe->rw_mutex, endt_us); + os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us); } } return call_stack; @@ -6784,30 +6803,47 @@ ASYNC_WORK_DEF(ctrl_call_stack_build_work) U64 pre_reg_gen = ctrl_reg_gen(); U64 pre_mem_gen = ctrl_mem_gen(); Arena *arena = arena_alloc(); - CTRL_Unwind unwind = ctrl_unwind_from_thread(arena, ctx, thread_handle, os_now_microseconds()+1000000); + CTRL_Unwind unwind = ctrl_unwind_from_thread(arena, ctx, thread_handle, 0); CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(arena, process, &unwind); U64 post_reg_gen = ctrl_reg_gen(); U64 post_mem_gen = ctrl_mem_gen(); - //- rjf: store in cache + //- rjf: store new results in cache Arena *last_arena = arena; - OS_MutexScopeW(stripe->rw_mutex) + if(pre_reg_gen == post_reg_gen && + pre_mem_gen == post_mem_gen) { - for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) + for(B32 done = 0; !done;) { - if(ctrl_handle_match(n->thread, thread_handle)) + B32 found = 0; + OS_MutexScopeW(stripe->rw_mutex) { - if(pre_reg_gen == post_reg_gen && - pre_mem_gen == post_mem_gen) + for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) { - last_arena = n->arena; - n->arena = arena; - n->reg_gen = pre_reg_gen; - n->mem_gen = pre_mem_gen; - n->unwind = unwind; - n->call_stack = call_stack; + if(ctrl_handle_match(n->thread, thread_handle)) + { + found = 1; + if(n->scope_touch_count == 0) + { + done = 1; + if(unwind.flags == 0 || call_stack.frames_count >= n->call_stack.frames_count) + { + last_arena = n->arena; + n->arena = arena; + n->call_stack = call_stack; + } + if(unwind.flags == 0) + { + n->reg_gen = pre_reg_gen; + n->mem_gen = pre_mem_gen; + } + } + break; + } } - n->working_count -= 1; + } + if(!found) + { break; } } @@ -6819,6 +6855,16 @@ ASYNC_WORK_DEF(ctrl_call_stack_build_work) arena_release(last_arena); } + //- rjf: mark work as done + OS_MutexScopeW(stripe->rw_mutex) for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) + { + if(ctrl_handle_match(n->thread, thread_handle)) + { + ins_atomic_u64_dec_eval(&n->working_count); + break; + } + } + scratch_end(scratch); } diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 49bfa0cb..50196a58 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -266,7 +266,9 @@ typedef struct CTRL_CallStack CTRL_CallStack; struct CTRL_CallStack { CTRL_CallStackFrame *frames; - U64 count; + U64 frames_count; + CTRL_CallStackFrame **concrete_frames; + U64 concrete_frames_count; }; //////////////////////////////// @@ -609,13 +611,18 @@ struct CTRL_CallStackCacheNode { CTRL_CallStackCacheNode *next; CTRL_CallStackCacheNode *prev; + + // rjf: key CTRL_Handle thread; - U64 scope_touch_count; - U64 working_count; - Arena *arena; U64 reg_gen; U64 mem_gen; - CTRL_Unwind unwind; + + // rjf: refcounts + U64 scope_touch_count; + U64 working_count; + + // rjf: value + Arena *arena; CTRL_CallStack call_stack; }; diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 04fb7fe3..d0f0527f 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1815,11 +1815,12 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) }break; case CTRL_EntityKind_Thread: { - CTRL_Unwind unwind = d_query_cached_unwind_from_thread(entity); - U64 frame_idx = e_interpret_ctx->reg_unwind_count; - if(frame_idx < unwind.frames.count) + CTRL_Scope *ctrl_scope = ctrl_scope_open(); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity, 0); + U64 concrete_frame_idx = e_interpret_ctx->reg_unwind_count; + if(concrete_frame_idx < call_stack.concrete_frames_count) { - CTRL_UnwindFrame *f = &unwind.frames.v[frame_idx]; + CTRL_CallStackFrame *f = call_stack.concrete_frames[concrete_frame_idx]; U64 regs_size = regs_block_size_from_arch(e_interpret_ctx->reg_arch); Rng1U64 legal_range = r1u64(0, regs_size); Rng1U64 read_range = intersect_1u64(legal_range, range); @@ -1827,6 +1828,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) MemoryCopy(out, (U8 *)f->regs + read_range.min, read_size); result = (read_size == dim_1u64(range)); } + ctrl_scope_close(ctrl_scope); }break; } }break; @@ -6393,12 +6395,12 @@ rd_window_frame(void) // rjf: unwind if(ctrl_entity->kind == CTRL_EntityKind_Thread) RD_Font(RD_FontSlot_Code) { + CTRL_Scope *ctrl_scope = ctrl_scope_open(); Vec4F32 code_color = ui_color_from_name(str8_lit("code_default")); Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol")); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process); - CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(ctrl_entity); - CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(scratch.arena, process, &base_unwind); - if(call_stack.count != 0) + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, ctrl_entity, 0); + if(call_stack.frames_count != 0) { ui_spacer(ui_em(1.5f, 1.f)); } @@ -6414,6 +6416,7 @@ rd_window_frame(void) String8 rip_value_string = rd_value_string_from_eval(scratch.arena, str8_zero(), &string_params, ui_top_font(), ui_top_font_size(), ui_top_font_size()*40.f, rip_eval); rd_code_label(1, 0, code_color, rip_value_string); } + ctrl_scope_close(ctrl_scope); } }break; @@ -11238,10 +11241,12 @@ rd_frame(void) } ////////////////////////////// - //- rjf: open frame debug info scope + //- rjf: open frame scopes // + if(rd_state->frame_depth == 0) { rd_state->frame_di_scope = di_scope_open(); + rd_state->frame_ctrl_scope = ctrl_scope_open(); } ////////////////////////////// @@ -11283,7 +11288,7 @@ rd_frame(void) U64 candidate_frame_time_us = 1000000/(U64)candidate; S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us; if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff && - frame_time_history_avg_us < candidate_frame_time_us + 1000) + frame_time_history_avg_us < candidate_frame_time_us + candidate_frame_time_us/4) { best_target_hz = candidate; best_target_hz_frame_time_us_diff = frame_time_us_diff; @@ -11613,7 +11618,6 @@ rd_frame(void) Arch arch = thread->arch; U64 unwind_count = rd_regs()->unwind_count; U64 rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_count); - CTRL_Unwind unwind = d_query_cached_unwind_from_thread(thread); CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr); U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr); U64 tls_root_vaddr = ctrl_tls_root_vaddr_from_thread(&d_state->ctrl_entity_store->ctx, thread->handle); @@ -15634,10 +15638,9 @@ rd_frame(void) }break; case RD_CmdKind_SelectUnwind: { + CTRL_Scope *ctrl_scope = ctrl_scope_open(); CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread); - CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process); - CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(scratch.arena, process, &base_unwind); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread, os_now_microseconds()+10000); CTRL_CallStackFrame *frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth); if(frame == 0) { @@ -15649,14 +15652,14 @@ rd_frame(void) rd_state->base_regs.v.inline_depth = rd_regs()->inline_depth; } rd_cmd(RD_CmdKind_FindThread, .thread = thread->handle, .unwind_count = rd_state->base_regs.v.unwind_count, .inline_depth = rd_state->base_regs.v.inline_depth); + ctrl_scope_close(ctrl_scope); }break; case RD_CmdKind_UpOneFrame: case RD_CmdKind_DownOneFrame: { + CTRL_Scope *ctrl_scope = ctrl_scope_open(); CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread); - CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process); - CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(scratch.arena, process, &base_unwind); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread, os_now_microseconds()+10000); CTRL_CallStackFrame *current_frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth); CTRL_CallStackFrame *next_frame = current_frame; if(current_frame != 0) switch(kind) @@ -15668,7 +15671,7 @@ rd_frame(void) next_frame = current_frame-1; }break; case RD_CmdKind_DownOneFrame: - if(current_frame+1 < call_stack.frames + call_stack.count) + if(current_frame+1 < call_stack.frames + call_stack.frames_count) { next_frame = current_frame+1; }break; @@ -15680,6 +15683,7 @@ rd_frame(void) .unwind_count = next_frame->unwind_count, .inline_depth = next_frame->inline_depth); } + ctrl_scope_close(ctrl_scope); }break; //- rjf: meta controls @@ -16700,10 +16704,12 @@ rd_frame(void) } ////////////////////////////// - //- rjf: close frame debug info scope + //- rjf: close frame scopes // + if(rd_state->frame_depth == 0) { di_scope_close(rd_state->frame_di_scope); + ctrl_scope_close(rd_state->frame_ctrl_scope); } ////////////////////////////// diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index 790b9904..3e4335c6 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -599,6 +599,7 @@ struct RD_State // rjf: frame parameters F32 frame_dt; DI_Scope *frame_di_scope; + CTRL_Scope *frame_ctrl_scope; // rjf: dbgi match store DI_MatchStore *match_store; diff --git a/src/raddbg/raddbg_eval.c b/src/raddbg/raddbg_eval.c index f1d37d5c..a329257e 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -955,11 +955,9 @@ E_TYPE_IREXT_FUNCTION_DEF(call_stack) CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(interp.space); if(entity->kind == CTRL_EntityKind_Thread) { - CTRL_Entity *process = ctrl_process_from_entity(entity); - CTRL_Unwind base_unwind = d_query_cached_unwind_from_thread(entity); accel->arch = entity->arch; - accel->process = process->handle; - accel->call_stack = ctrl_call_stack_from_unwind(arena, process, &base_unwind); + accel->process = ctrl_process_from_entity(entity)->handle; + accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, entity, 0); } scratch_end(scratch); } @@ -975,7 +973,7 @@ E_TYPE_ACCESS_FUNCTION_DEF(call_stack) RD_CallStackAccel *accel = (RD_CallStackAccel *)lhs_irtree->user_data; E_Value rhs_value = e_value_from_expr(expr->first->next); CTRL_CallStack *call_stack = &accel->call_stack; - if(0 <= rhs_value.u64 && rhs_value.u64 < call_stack->count) + if(0 <= rhs_value.u64 && rhs_value.u64 < call_stack->frames_count) { CTRL_Entity *process = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, accel->process); CTRL_CallStackFrame *f = &call_stack->frames[rhs_value.u64]; @@ -992,7 +990,7 @@ E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack) RD_CallStackAccel *accel = (RD_CallStackAccel *)eval.irtree.user_data; E_TypeExpandInfo result = {0}; result.user_data = accel; - result.expr_count = accel->call_stack.count; + result.expr_count = accel->call_stack.frames_count; return result; } diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 72413ee0..81fdc192 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -1008,17 +1008,18 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row) CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(block_eval.space); if(entity->kind == CTRL_EntityKind_Thread) { + CTRL_Scope *ctrl_scope = ctrl_scope_open(); info.callstack_thread = entity; U64 frame_num = ev_block_num_from_id(block, key.child_id); - CTRL_Unwind unwind = d_query_cached_unwind_from_thread(entity); - CTRL_CallStack call_stack = ctrl_call_stack_from_unwind(scratch.arena, ctrl_process_from_entity(entity), &unwind); - if(1 <= frame_num && frame_num <= call_stack.count) + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity, 0); + if(1 <= frame_num && frame_num <= call_stack.frames_count) { CTRL_CallStackFrame *f = &call_stack.frames[frame_num-1]; info.callstack_unwind_index = f->unwind_count; info.callstack_inline_depth = f->inline_depth; info.callstack_vaddr = regs_rip_from_arch_block(entity->arch, f->regs); } + ctrl_scope_close(ctrl_scope); } } @@ -2837,17 +2838,18 @@ RD_VIEW_UI_FUNCTION_DEF(memory) }; AnnotationList *visible_memory_annotations = push_array(scratch.arena, AnnotationList, visible_memory_size); { + CTRL_Scope *ctrl_scope = ctrl_scope_open(); CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_regs()->thread); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(thread, CTRL_EntityKind_Process); - CTRL_Unwind unwind = d_query_cached_unwind_from_thread(thread); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread, 0); //- rjf: fill unwind frame annotations - if(unwind.frames.count != 0) UI_Tag(str8_lit("weak")) + if(call_stack.concrete_frames_count != 0) UI_Tag(str8_lit("weak")) { - U64 last_stack_top = regs_rsp_from_arch_block(thread->arch, unwind.frames.v[0].regs); - for(U64 idx = 1; idx < unwind.frames.count; idx += 1) + U64 last_stack_top = regs_rsp_from_arch_block(thread->arch, call_stack.concrete_frames[0]->regs); + for(U64 idx = 1; idx < call_stack.concrete_frames_count; idx += 1) { - CTRL_UnwindFrame *f = &unwind.frames.v[idx]; + CTRL_CallStackFrame *f = call_stack.concrete_frames[idx]; U64 f_stack_top = regs_rsp_from_arch_block(thread->arch, f->regs); Rng1U64 frame_vaddr_range = r1u64(last_stack_top, f_stack_top); Rng1U64 frame_vaddr_range_in_viz = intersect_1u64(frame_vaddr_range, viz_range_bytes); @@ -2896,10 +2898,10 @@ RD_VIEW_UI_FUNCTION_DEF(memory) } //- rjf: fill selected thread stack range annotation - if(unwind.frames.count > 0) + if(call_stack.concrete_frames_count > 0) { U64 stack_base_vaddr = thread->stack_base; - U64 stack_top_vaddr = regs_rsp_from_arch_block(thread->arch, unwind.frames.v[0].regs); + U64 stack_top_vaddr = regs_rsp_from_arch_block(thread->arch, call_stack.concrete_frames[0]->regs); Rng1U64 stack_vaddr_range = r1u64(stack_base_vaddr, stack_top_vaddr); Rng1U64 stack_vaddr_range_in_viz = intersect_1u64(stack_vaddr_range, viz_range_bytes); if(dim_1u64(stack_vaddr_range_in_viz) != 0) @@ -2954,6 +2956,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) } di_scope_close(scope); } + ctrl_scope_close(ctrl_scope); } ////////////////////////////// diff --git a/src/raddbg/raddbg_widgets.c b/src/raddbg/raddbg_widgets.c index 689e215c..1257da53 100644 --- a/src/raddbg/raddbg_widgets.c +++ b/src/raddbg/raddbg_widgets.c @@ -521,13 +521,14 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e { Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol")); dr_fstrs_push_new(arena, &result, ¶ms, str8_lit(" ")); + CTRL_Scope *ctrl_scope = ctrl_scope_open(); DI_Scope *di_scope = di_scope_open(); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process); Arch arch = entity->arch; - CTRL_Unwind unwind = d_query_cached_unwind_from_thread(entity); - for(U64 idx = 0, limit = 6; idx < unwind.frames.count && idx < limit; idx += 1) + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity, 0); + for(U64 idx = 0, limit = 6; idx < call_stack.frames_count && idx < limit; idx += 1) { - CTRL_UnwindFrame *f = &unwind.frames.v[unwind.frames.count - 1 - idx]; + CTRL_CallStackFrame *f = &call_stack.frames[call_stack.frames_count - 1 - idx]; U64 rip_vaddr = regs_rip_from_arch_block(arch, f->regs); CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr); U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr); @@ -542,7 +543,7 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e if(name.size != 0) { dr_fstrs_push_new(arena, &result, ¶ms, name, .size = extras_size, .color = symbol_color); - if(idx+1 < unwind.frames.count) + if(idx+1 < call_stack.frames_count) { dr_fstrs_push_new(arena, &result, ¶ms, str8_lit(" > "), .color = secondary_color, .size = extras_size); if(idx+1 == limit) @@ -554,6 +555,7 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e } } di_scope_close(di_scope); + ctrl_scope_close(ctrl_scope); } //- rjf: modules get debug info status extras