From 36dcd65a83897784e4a3823c9fb9eabf06832a7a Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Mon, 11 Aug 2025 16:08:04 -0700 Subject: [PATCH] first pass at call stack tree evaluation --- src/ctrl/ctrl_core.c | 27 ++++++++++++++ src/ctrl/ctrl_core.h | 13 +++++++ src/raddbg/raddbg_core.c | 21 +++++++++++ src/raddbg/raddbg_core.h | 3 ++ src/raddbg/raddbg_eval.c | 80 +++++++++++++++++++++++++++++++++++++++- src/raddbg/raddbg_eval.h | 1 + 6 files changed, 143 insertions(+), 2 deletions(-) diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index a434ceca..5fe76704 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -188,6 +188,22 @@ ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src) return dst; } +internal CTRL_HandleArray +ctrl_handle_array_from_list(Arena *arena, CTRL_HandleList *src) +{ + CTRL_HandleArray array = {0}; + array.count = src->count; + array.v = push_array_no_zero(arena, CTRL_Handle, array.count); + { + U64 idx = 0; + for(CTRL_HandleNode *n = src->first; n != 0; n = n->next, idx += 1) + { + array.v[idx] = n->v; + } + } + return array; +} + internal String8 ctrl_string_from_handle(Arena *arena, CTRL_Handle handle) { @@ -1543,6 +1559,9 @@ ctrl_init(void) ctrl_state->module_image_info_cache.stripes[idx].arena = arena_alloc(); ctrl_state->module_image_info_cache.stripes[idx].rw_mutex = os_rw_mutex_alloc(); } + ctrl_state->call_stack_tree_cache.tree.root = &ctrl_call_stack_tree_node_nil; + ctrl_state->call_stack_tree_cache.cv = os_condition_variable_alloc(); + ctrl_state->call_stack_tree_cache.rw_mutex = os_rw_mutex_alloc(); ctrl_state->u2c_ring_size = KB(64); ctrl_state->u2c_ring_base = push_array_no_zero(arena, U8, ctrl_state->u2c_ring_size); ctrl_state->u2c_ring_mutex = os_mutex_alloc(); @@ -7358,9 +7377,14 @@ ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work) if(pre_mem_gen == post_mem_gen && pre_reg_gen == post_reg_gen) { + U64 id_gen = 0; arena = arena_alloc(); tree.root = push_array(arena, CTRL_CallStackTreeNode, 1); MemoryCopyStruct(tree.root, &ctrl_call_stack_tree_node_nil); + tree.root->id = id_gen; + tree.slots_count = Max(1, threads_count); + tree.slots = push_array(arena, CTRL_CallStackTreeNode *, tree.slots_count); + id_gen += 1; for EachIndex(thread_idx, threads_count) { CTRL_Handle thread = threads[thread_idx]; @@ -7385,6 +7409,9 @@ ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work) { next_node = push_array(arena, CTRL_CallStackTreeNode, 1); MemoryCopyStruct(next_node, &ctrl_call_stack_tree_node_nil); + next_node->id = id_gen; + SLLStackPush_N(tree.slots[next_node->id%tree.slots_count], next_node, hash_next); + id_gen += 1; SLLQueuePush_NZ(&ctrl_call_stack_tree_node_nil, thread_node->first, thread_node->last, next_node, next); next_node->parent = thread_node; thread_node->child_count += 1; diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 48701ef7..b5d6e612 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -84,6 +84,13 @@ struct CTRL_HandleList U64 count; }; +typedef struct CTRL_HandleArray CTRL_HandleArray; +struct CTRL_HandleArray +{ + CTRL_Handle *v; + U64 count; +}; + //////////////////////////////// //~ rjf: Generated Code @@ -277,11 +284,13 @@ struct CTRL_CallStack typedef struct CTRL_CallStackTreeNode CTRL_CallStackTreeNode; struct CTRL_CallStackTreeNode { + CTRL_CallStackTreeNode *hash_next; CTRL_CallStackTreeNode *first; CTRL_CallStackTreeNode *last; CTRL_CallStackTreeNode *next; CTRL_CallStackTreeNode *parent; U64 child_count; + U64 id; CTRL_Handle process; U64 vaddr; U64 depth; @@ -293,6 +302,8 @@ typedef struct CTRL_CallStackTree CTRL_CallStackTree; struct CTRL_CallStackTree { CTRL_CallStackTreeNode *root; + U64 slots_count; + CTRL_CallStackTreeNode **slots; }; //////////////////////////////// @@ -911,6 +922,7 @@ read_only global CTRL_Entity ctrl_entity_nil = }; read_only global CTRL_CallStackTreeNode ctrl_call_stack_tree_node_nil = { + 0, &ctrl_call_stack_tree_node_nil, &ctrl_call_stack_tree_node_nil, &ctrl_call_stack_tree_node_nil, @@ -945,6 +957,7 @@ internal CTRL_Handle ctrl_handle_make(CTRL_MachineID machine_id, DMN_Handle dmn_ internal B32 ctrl_handle_match(CTRL_Handle a, CTRL_Handle b); internal void ctrl_handle_list_push(Arena *arena, CTRL_HandleList *list, CTRL_Handle *pair); internal CTRL_HandleList ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src); +internal CTRL_HandleArray ctrl_handle_array_from_list(Arena *arena, CTRL_HandleList *src); internal String8 ctrl_string_from_handle(Arena *arena, CTRL_Handle handle); internal CTRL_Handle ctrl_handle_from_string(String8 string); diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index f15ce2fa..5ba92c99 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -11499,6 +11499,7 @@ rd_frame(void) CTRL_Scope *frame_ctrl_scope_restore = rd_state->frame_ctrl_scope; rd_state->frame_di_scope = di_scope_open(); rd_state->frame_ctrl_scope = ctrl_scope_open(); + rd_state->got_frame_call_stack_tree = 0; ////////////////////////////// //- rjf: calculate avg length in us of last many frames @@ -12296,6 +12297,26 @@ rd_frame(void) e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, collection_name, collection_type_key); } + //- rjf: add macro for call stack tree + { + String8 collection_name = str8_lit("call_stack_tree"); + E_TypeKey collection_type_key = e_type_key_cons(.kind = E_TypeKind_Set, + .name = collection_name, + .flags = E_TypeFlag_StubSingleLineExpansion, + .access = E_TYPE_ACCESS_FUNCTION_NAME(call_stack_tree), + .expand = + { + .info = E_TYPE_EXPAND_INFO_FUNCTION_NAME(call_stack_tree), + .range = E_TYPE_EXPAND_RANGE_FUNCTION_NAME(call_stack_tree) + }); + E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafValue, r1u64(0, 0)); + expr->value.u64 = 1; + expr->type_key = collection_type_key; + expr->space = e_space_make(RD_EvalSpaceKind_MetaCallStackTree); + e_string2expr_map_insert(scratch.arena, macro_map, collection_name, expr); + e_string2typekey_map_insert(rd_frame_arena(), rd_state->meta_name2type_map, collection_name, collection_type_key); + } + //- rjf: add macro / lookup rules for unattached processes { String8 collection_name = str8_lit("unattached_processes"); diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index adc9f532..2cdcabc0 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -88,6 +88,7 @@ enum RD_EvalSpaceKind_MetaTheme, RD_EvalSpaceKind_MetaCtrlEntity, RD_EvalSpaceKind_MetaUnattachedProcess, + RD_EvalSpaceKind_MetaCallStackTree, }; //////////////////////////////// @@ -600,6 +601,8 @@ struct RD_State F32 frame_dt; DI_Scope *frame_di_scope; CTRL_Scope *frame_ctrl_scope; + CTRL_CallStackTree frame_call_stack_tree; + B32 got_frame_call_stack_tree; // rjf: dbgi match store DI_MatchStore *match_store; diff --git a/src/raddbg/raddbg_eval.c b/src/raddbg/raddbg_eval.c index 7ecf2d77..c1795d34 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -1541,14 +1541,90 @@ E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities) //////////////////////////////// //~ rjf: Call Stack Tree Type Hooks +E_TYPE_ACCESS_FUNCTION_DEF(call_stack_tree) +{ + E_IRTreeAndType result = {&e_irnode_nil}; + if(expr->kind == E_ExprKind_MemberAccess) + { + String8 id_string = expr->first->next->string; + U64 id = u64_from_str8(str8_skip(id_string, 1), 16); + if(id != 0) + { + result.type_key = lhs_irtree->type_key; + result.mode = E_Mode_Value; + result.root = e_irtree_set_space(arena, e_space_make(RD_EvalSpaceKind_MetaCallStackTree), e_irtree_const_u(arena, id)); + } + } + return result; +} + +typedef struct RD_CallStackTreeExpandAccel RD_CallStackTreeExpandAccel; +struct RD_CallStackTreeExpandAccel +{ + CTRL_CallStackTreeNode *node; + CTRL_CallStackTreeNode **children; + CTRL_HandleArray threads; +}; + E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree) { - + if(!rd_state->got_frame_call_stack_tree) + { + rd_state->got_frame_call_stack_tree = 1; + rd_state->frame_call_stack_tree = ctrl_call_stack_tree(rd_state->frame_ctrl_scope, 0); + } + RD_CallStackTreeExpandAccel *accel = push_array(arena, RD_CallStackTreeExpandAccel, 1); + accel->node = &ctrl_call_stack_tree_node_nil; + U64 id = e_value_eval_from_eval(eval).value.u64; + if(rd_state->frame_call_stack_tree.slots_count != 0) + { + for(CTRL_CallStackTreeNode *n = rd_state->frame_call_stack_tree.slots[id%rd_state->frame_call_stack_tree.slots_count]; + n != 0; + n = n->hash_next) + { + if(n->id == id) + { + accel->node = n; + break; + } + } + } + accel->children = push_array(arena, CTRL_CallStackTreeNode *, accel->node->child_count); + { + U64 idx = 0; + for(CTRL_CallStackTreeNode *n = accel->node->first; + n != &ctrl_call_stack_tree_node_nil; + n = n->next, idx += 1) + { + accel->children[idx] = n; + } + } + accel->threads = ctrl_handle_array_from_list(arena, &accel->node->threads); + E_TypeExpandInfo result = {accel}; + result.expr_count = accel->node->child_count + accel->threads.count; + return result; } E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree) { - + Temp scratch = scratch_begin(&arena, 1); + RD_CallStackTreeExpandAccel *accel = (RD_CallStackTreeExpandAccel *)user_data; + CTRL_CallStackTreeNode *node = accel->node; + U64 needed_row_count = dim_1u64(idx_range); + for EachIndex(idx, needed_row_count) + { + E_Eval eval = e_eval_nil; + if(idx < node->child_count) + { + eval = e_eval_from_stringf("query:call_stack_tree.$%I64x", accel->children[idx]->id); + } + else if(node->child_count <= idx && idx < node->child_count + accel->threads.count) + { + eval = e_eval_from_stringf("query:control.%S", ctrl_string_from_handle(scratch.arena, accel->threads.v[idx - node->child_count])); + } + evals_out[idx] = eval; + } + scratch_end(scratch); } //////////////////////////////// diff --git a/src/raddbg/raddbg_eval.h b/src/raddbg/raddbg_eval.h index 3a04761c..b2269be9 100644 --- a/src/raddbg/raddbg_eval.h +++ b/src/raddbg/raddbg_eval.h @@ -103,6 +103,7 @@ E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities); //////////////////////////////// //~ rjf: Call Stack Tree Type Hooks +E_TYPE_ACCESS_FUNCTION_DEF(call_stack_tree); E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree); E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree);