From 1d8abb19068f01ad78dd4ccfda5857943adf883f Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Thu, 23 Oct 2025 13:55:51 -0700 Subject: [PATCH] switch to tighter cache for loaded debug infos --- src/raddbg/raddbg_core.c | 103 +++++++++++++++++++++------------------ src/raddbg/raddbg_core.h | 25 ++++++++++ 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index cd4405a7..0f2c5b13 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -9992,6 +9992,12 @@ rd_init(CmdLine *cmdln) cfg_node_new(rd_state->cfg, cfg_node_root(), str8_lit("transient")); } + // rjf: set up loaded debug info cache + { + rd_state->loaded_dbg_info_slots_count = 4096; + rd_state->loaded_dbg_info_slots = push_array(arena, RD_LoadedDbgInfoSlot, rd_state->loaded_dbg_info_slots_count); + } + // rjf: set up window cache { rd_state->window_state_slots_count = 64; @@ -10355,69 +10361,72 @@ rd_frame(void) } ////////////////////////////// - //- rjf: iterate all loaded debug infos, remove hot markers + //- rjf: apply debug info config trees -> loaded debug info cache // - if(rd_state->frame_depth == 1) { - CFG_Node *transient = cfg_node_child_from_string(cfg_node_root(), str8_lit("transient")); - CFG_Node *loaded_debug_infos = cfg_node_child_from_string_or_alloc(rd_state->cfg, transient, str8_lit("loaded_debug_infos")); - for(CFG_Node *child = loaded_debug_infos->first; child != &cfg_nil_node; child = child->next) - { - cfg_node_release(rd_state->cfg, cfg_node_child_from_string(child, str8_lit("hot"))); - } - } - - ////////////////////////////// - //- rjf: iterate all loaded debug infos, touch their dbgi load markers - // - if(rd_state->frame_depth == 1) - { - CFG_Node *transient = cfg_node_child_from_string(cfg_node_root(), str8_lit("transient")); - CFG_Node *loaded_debug_infos = cfg_node_child_from_string_or_alloc(rd_state->cfg, transient, str8_lit("loaded_debug_infos")); + U64 current_update_tick_idx = update_tick_idx(); + + //- rjf: for each debug info config, reflect in cache - open if needed CFG_NodePtrList dbg_infos = cfg_node_top_level_list_from_string(scratch.arena, str8_lit("debug_info")); for EachNode(n, CFG_NodePtrNode, dbg_infos.first) { + // rjf: unpack debug info config CFG_Node *di = n->v; String8 path = rd_path_from_cfg(di); CFG_Node *di_timestamp = cfg_node_child_from_string(di, str8_lit("timestamp")); U64 timestamp = 0; try_u64_from_str8_c_rules(di_timestamp->first->string, ×tamp); - String8 loaded_di_key = push_str8f(scratch.arena, "$%I64x `%S` `%I64u`", di->id, path, timestamp); - CFG_Node *loaded_di = cfg_node_child_from_string(loaded_debug_infos, loaded_di_key); - if(loaded_di == &cfg_nil_node) + DI_Key key = di_key_from_path_timestamp(path, timestamp); + + // rjf: touch in cache + U64 hash = u64_hash_from_str8(str8_struct(&key)); + U64 slot_idx = hash%rd_state->loaded_dbg_info_slots_count; + RD_LoadedDbgInfoSlot *slot = &rd_state->loaded_dbg_info_slots[slot_idx]; + RD_LoadedDbgInfoNode *node = 0; + for(RD_LoadedDbgInfoNode *n = slot->first; n != 0; n = n->hash_next) { - loaded_di = cfg_node_new(rd_state->cfg, loaded_debug_infos, loaded_di_key); - CFG_Node *path_node = cfg_node_new(rd_state->cfg, loaded_di, str8_lit("path")); - cfg_node_new(rd_state->cfg, path_node, path); - CFG_Node *timestamp_node = cfg_node_new(rd_state->cfg, loaded_di, str8_lit("timestamp")); - cfg_node_new(rd_state->cfg, timestamp_node, di_timestamp->first->string); - DI_Key dbgi_key = di_key_from_path_timestamp(path, timestamp); - di_open(dbgi_key); + if(di_key_match(key, n->key)) + { + node = n; + break; + } } - cfg_node_child_from_string_or_alloc(rd_state->cfg, loaded_di, str8_lit("hot")); + if(node == 0) + { + node = rd_state->free_loaded_dbg_info_node; + if(node) + { + SLLStackPop_N(rd_state->free_loaded_dbg_info_node, hash_next); + } + else + { + node = push_array(rd_state->arena, RD_LoadedDbgInfoNode, 1); + } + DLLPushBack_NP(slot->first, slot->last, node, hash_next, hash_prev); + node->key = key; + di_open(key); + } + node->last_tick_idx_touched = current_update_tick_idx; + DLLRemove_NP(rd_state->loaded_dbg_info_lru_first, rd_state->loaded_dbg_info_lru_last, node, lru_next, lru_prev); + DLLPushBack_NP(rd_state->loaded_dbg_info_lru_first, rd_state->loaded_dbg_info_lru_last, node, lru_next, lru_prev); } - } - - ////////////////////////////// - //- rjf: iterate all loaded debug infos, close those without hot markers - // - if(rd_state->frame_depth == 1) - { - CFG_Node *transient = cfg_node_child_from_string(cfg_node_root(), str8_lit("transient")); - CFG_Node *loaded_debug_infos = cfg_node_child_from_string_or_alloc(rd_state->cfg, transient, str8_lit("loaded_debug_infos")); - for(CFG_Node *child = loaded_debug_infos->first, *next = &cfg_nil_node; child != &cfg_nil_node; child = next) + + //- rjf: iterate least-recently-used loaded debug infos - if any have not been updated this tick, + // then evict + for(RD_LoadedDbgInfoNode *n = rd_state->loaded_dbg_info_lru_first, *next = 0; n != 0; n = next) { - next = child->next; - if(cfg_node_child_from_string(child, str8_lit("hot")) == &cfg_nil_node) + next = n->lru_next; + if(n->last_tick_idx_touched >= current_update_tick_idx) { - CFG_Node *path_node = cfg_node_child_from_string(child, str8_lit("path")); - CFG_Node *timestamp_node = cfg_node_child_from_string(child, str8_lit("timestamp")); - U64 timestamp = 0; - try_u64_from_str8_c_rules(timestamp_node->first->string, ×tamp); - DI_Key dbgi_key = di_key_from_path_timestamp(path_node->first->string, timestamp); - di_close(dbgi_key, 0); - cfg_node_release(rd_state->cfg, child); + break; } + U64 hash = u64_hash_from_str8(str8_struct(&n->key)); + U64 slot_idx = hash%rd_state->loaded_dbg_info_slots_count; + RD_LoadedDbgInfoSlot *slot = &rd_state->loaded_dbg_info_slots[slot_idx]; + DLLRemove_NP(rd_state->loaded_dbg_info_lru_first, rd_state->loaded_dbg_info_lru_last, n, lru_next, lru_prev); + DLLRemove_NP(slot->first, slot->last, n, hash_next, hash_prev); + SLLStackPush_N(rd_state->free_loaded_dbg_info_node, n, hash_next); + di_close(n->key, 0); } } diff --git a/src/raddbg/raddbg_core.h b/src/raddbg/raddbg_core.h index c18d216d..29315bf4 100644 --- a/src/raddbg/raddbg_core.h +++ b/src/raddbg/raddbg_core.h @@ -343,6 +343,24 @@ struct RD_WindowStateSlot //////////////////////////////// //~ rjf: Main Per-Process Graphical State +typedef struct RD_LoadedDbgInfoNode RD_LoadedDbgInfoNode; +struct RD_LoadedDbgInfoNode +{ + RD_LoadedDbgInfoNode *hash_next; + RD_LoadedDbgInfoNode *hash_prev; + RD_LoadedDbgInfoNode *lru_next; + RD_LoadedDbgInfoNode *lru_prev; + DI_Key key; + U64 last_tick_idx_touched; +}; + +typedef struct RD_LoadedDbgInfoSlot RD_LoadedDbgInfoSlot; +struct RD_LoadedDbgInfoSlot +{ + RD_LoadedDbgInfoNode *first; + RD_LoadedDbgInfoNode *last; +}; + typedef struct RD_AmbiguousPathNode RD_AmbiguousPathNode; struct RD_AmbiguousPathNode { @@ -480,6 +498,13 @@ struct RD_State CFG_State *cfg; CFG_SchemaTable *cfg_schema_table; + // rjf: loaded debug info cache + U64 loaded_dbg_info_slots_count; + RD_LoadedDbgInfoSlot *loaded_dbg_info_slots; + RD_LoadedDbgInfoNode *loaded_dbg_info_lru_first; + RD_LoadedDbgInfoNode *loaded_dbg_info_lru_last; + RD_LoadedDbgInfoNode *free_loaded_dbg_info_node; + // rjf: window state cache U64 window_state_slots_count; RD_WindowStateSlot *window_state_slots;