mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-13 07:32:23 -07:00
eliminate old async call stack computation / call stack cache code
This commit is contained in:
@@ -1432,70 +1432,6 @@ ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *lis
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Accessing Scopes
|
||||
|
||||
internal CTRL_Scope *
|
||||
ctrl_scope_open(void)
|
||||
{
|
||||
if(ctrl_tctx == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
ctrl_tctx = push_array(arena, CTRL_TCTX, 1);
|
||||
ctrl_tctx->arena = arena;
|
||||
}
|
||||
CTRL_Scope *scope = ctrl_tctx->free_scope;
|
||||
if(scope != 0)
|
||||
{
|
||||
SLLStackPop(ctrl_tctx->free_scope);
|
||||
}
|
||||
else
|
||||
{
|
||||
scope = push_array_no_zero(ctrl_tctx->arena, CTRL_Scope, 1);
|
||||
}
|
||||
MemoryZeroStruct(scope);
|
||||
return scope;
|
||||
}
|
||||
|
||||
internal void
|
||||
ctrl_scope_close(CTRL_Scope *scope)
|
||||
{
|
||||
for(CTRL_ScopeCallStackTouch *t = scope->first_call_stack_touch, *next = 0; t != 0; t = next)
|
||||
{
|
||||
next = t->next;
|
||||
ins_atomic_u64_dec_eval(&t->node->scope_touch_count);
|
||||
cond_var_broadcast(t->stripe->cv);
|
||||
SLLStackPush(ctrl_tctx->free_call_stack_touch, t);
|
||||
}
|
||||
for(U64 idx = 0; idx < scope->call_stack_tree_touch_count; idx += 1)
|
||||
{
|
||||
ins_atomic_u64_dec_eval(&ctrl_state->call_stack_tree_cache.scope_touch_count);
|
||||
}
|
||||
if(scope->call_stack_tree_touch_count != 0)
|
||||
{
|
||||
cond_var_broadcast(ctrl_state->call_stack_tree_cache.cv);
|
||||
}
|
||||
SLLStackPush(ctrl_tctx->free_scope, scope);
|
||||
}
|
||||
|
||||
internal void
|
||||
ctrl_scope_touch_call_stack_node__stripe_r_guarded(CTRL_Scope *scope, CTRL_CallStackCacheStripe *stripe, CTRL_CallStackCacheNode *node)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(&node->scope_touch_count);
|
||||
CTRL_ScopeCallStackTouch *touch = ctrl_tctx->free_call_stack_touch;
|
||||
if(touch != 0)
|
||||
{
|
||||
SLLStackPop(ctrl_tctx->free_call_stack_touch);
|
||||
}
|
||||
else
|
||||
{
|
||||
touch = push_array(ctrl_tctx->arena, CTRL_ScopeCallStackTouch, 1);
|
||||
}
|
||||
SLLQueuePush(scope->first_call_stack_touch, scope->last_call_stack_touch, touch);
|
||||
touch->stripe = stripe;
|
||||
touch->node = node;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
@@ -1540,16 +1476,6 @@ ctrl_init(void)
|
||||
ctrl_state->thread_reg_cache.stripes[idx].arena = arena_alloc();
|
||||
ctrl_state->thread_reg_cache.stripes[idx].rw_mutex = rw_mutex_alloc();
|
||||
}
|
||||
ctrl_state->call_stack_cache.slots_count = 1024;
|
||||
ctrl_state->call_stack_cache.slots = push_array(arena, CTRL_CallStackCacheSlot, ctrl_state->call_stack_cache.slots_count);
|
||||
ctrl_state->call_stack_cache.stripes_count = os_get_system_info()->logical_processor_count;
|
||||
ctrl_state->call_stack_cache.stripes = push_array(arena, CTRL_CallStackCacheStripe, ctrl_state->call_stack_cache.stripes_count);
|
||||
for(U64 idx = 0; idx < ctrl_state->call_stack_cache.stripes_count; idx += 1)
|
||||
{
|
||||
ctrl_state->call_stack_cache.stripes[idx].arena = arena_alloc();
|
||||
ctrl_state->call_stack_cache.stripes[idx].rw_mutex = rw_mutex_alloc();
|
||||
ctrl_state->call_stack_cache.stripes[idx].cv = cond_var_alloc();
|
||||
}
|
||||
ctrl_state->module_image_info_cache.slots_count = 1024;
|
||||
ctrl_state->module_image_info_cache.slots = push_array(arena, CTRL_ModuleImageInfoCacheSlot, ctrl_state->module_image_info_cache.slots_count);
|
||||
ctrl_state->module_image_info_cache.stripes_count = os_get_system_info()->logical_processor_count;
|
||||
@@ -1600,10 +1526,6 @@ ctrl_init(void)
|
||||
ctrl_state->u2ms_ring_base = push_array(arena, U8, ctrl_state->u2ms_ring_size);
|
||||
ctrl_state->u2ms_ring_mutex = mutex_alloc();
|
||||
ctrl_state->u2ms_ring_cv = cond_var_alloc();
|
||||
ctrl_state->u2csb_ring_size = KB(64);
|
||||
ctrl_state->u2csb_ring_base = push_array(arena, U8, ctrl_state->u2csb_ring_size);
|
||||
ctrl_state->u2csb_ring_mutex = mutex_alloc();
|
||||
ctrl_state->u2csb_ring_cv = cond_var_alloc();
|
||||
ctrl_state->ctrl_thread_log = log_alloc();
|
||||
ctrl_state->ctrl_thread = thread_launch(ctrl_thread__entry_point, 0);
|
||||
}
|
||||
@@ -3428,157 +3350,6 @@ ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U
|
||||
return f;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Cache Functions
|
||||
|
||||
internal CTRL_CallStack
|
||||
ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us)
|
||||
{
|
||||
CTRL_CallStack call_stack = {0};
|
||||
CTRL_CallStackCache *cache = &ctrl_state->call_stack_cache;
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: unpack thread
|
||||
//
|
||||
U64 hash = ctrl_hash_from_handle(thread_handle);
|
||||
U64 slot_idx = hash%cache->slots_count;
|
||||
U64 stripe_idx = slot_idx%cache->stripes_count;
|
||||
CTRL_CallStackCacheSlot *slot = &cache->slots[slot_idx];
|
||||
CTRL_CallStackCacheStripe *stripe = &cache->stripes[stripe_idx];
|
||||
U64 reg_gen = ctrl_reg_gen();
|
||||
U64 mem_gen = ctrl_mem_gen();
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: loop: try to grab cached call stack; request; wait
|
||||
//
|
||||
B32 can_request = !ins_atomic_u64_eval(&ctrl_state->ctrl_thread_run_state);
|
||||
for(U64 retry_idx = 0;; retry_idx += 1)
|
||||
{
|
||||
//- rjf: [read-only] try to look for current call stack; wait if working
|
||||
B32 node_exists = 0;
|
||||
B32 node_stale = 1;
|
||||
B32 node_working = 0;
|
||||
MutexScopeR(stripe->rw_mutex) for(;;)
|
||||
{
|
||||
CTRL_CallStackCacheNode *node = 0;
|
||||
for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(ctrl_handle_match(n->thread, thread_handle))
|
||||
{
|
||||
node = n;
|
||||
node_exists = 1;
|
||||
node_stale = (reg_gen > n->reg_gen || mem_gen > n->mem_gen);
|
||||
node_working = (n->working_count > 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node_exists && (!can_request || !node_stale || os_now_microseconds() >= endt_us))
|
||||
{
|
||||
call_stack = node->call_stack;
|
||||
ctrl_scope_touch_call_stack_node__stripe_r_guarded(scope, stripe, node);
|
||||
break;
|
||||
}
|
||||
else if(node_working)
|
||||
{
|
||||
cond_var_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: out of time, or got new result => exit
|
||||
if((retry_idx > 0 && os_now_microseconds() >= endt_us) || !node_stale)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//- rjf: [write] node does not exist => create; request if new or stale
|
||||
B32 need_request = (!node_exists || node_stale);
|
||||
CTRL_CallStackCacheNode *node_to_request = 0;
|
||||
if(can_request && need_request) MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
CTRL_CallStackCacheNode *node = 0;
|
||||
for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(ctrl_handle_match(n->thread, 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;
|
||||
}
|
||||
if(node->working_count == 0)
|
||||
{
|
||||
node->working_count += 1;
|
||||
node_to_request = node;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: request if needed
|
||||
if(node_to_request != 0)
|
||||
{
|
||||
if(ctrl_u2csb_enqueue_req(thread_handle, endt_us))
|
||||
{
|
||||
async_push_work(ctrl_call_stack_build_work, .priority = high_priority ? ASYNC_Priority_High : ASYNC_Priority_Low);
|
||||
}
|
||||
else MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
node_to_request->working_count -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return call_stack;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Tree Cache Functions
|
||||
|
||||
internal CTRL_CallStackTree
|
||||
ctrl_call_stack_tree(CTRL_Scope *scope, U64 endt_us)
|
||||
{
|
||||
CTRL_CallStackTree result = {&ctrl_call_stack_tree_node_nil};
|
||||
{
|
||||
U64 reg_gen = ctrl_reg_gen();
|
||||
U64 mem_gen = ctrl_mem_gen();
|
||||
CTRL_CallStackTreeCache *cache = &ctrl_state->call_stack_tree_cache;
|
||||
MutexScopeR(cache->rw_mutex) for(;;)
|
||||
{
|
||||
// rjf: unpack cache/time state
|
||||
B32 is_stale = (cache->reg_gen != reg_gen || cache->mem_gen != mem_gen);
|
||||
B32 out_of_time = (os_now_microseconds() >= endt_us);
|
||||
|
||||
// rjf: is stale? -> request new calculation
|
||||
if(is_stale && !ins_atomic_u64_eval_cond_assign(&cache->request_count, 1, 0))
|
||||
{
|
||||
rw_mutex_drop_r(cache->rw_mutex);
|
||||
async_push_work(ctrl_call_stack_tree_build_work);
|
||||
rw_mutex_take_r(cache->rw_mutex);
|
||||
}
|
||||
|
||||
// rjf: is not stale, or we're out of time? -> grab cached result & touch, exit
|
||||
if(!is_stale || out_of_time)
|
||||
{
|
||||
result = cache->tree;
|
||||
ins_atomic_u64_inc_eval(&cache->scope_touch_count);
|
||||
scope->call_stack_tree_touch_count += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: wait for new results
|
||||
cond_var_wait_rw_r(cache->cv, cache->rw_mutex, endt_us);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Halting All Attached Processes
|
||||
|
||||
@@ -7082,384 +6853,6 @@ ASYNC_WORK_DEF(ctrl_mem_stream_work)
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Unwinding Functions
|
||||
|
||||
//- rjf: user -> memory stream communication
|
||||
|
||||
internal B32
|
||||
ctrl_u2csb_enqueue_req(CTRL_Handle thread, U64 endt_us)
|
||||
{
|
||||
B32 good = 0;
|
||||
MutexScope(ctrl_state->u2csb_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = ctrl_state->u2csb_ring_write_pos - ctrl_state->u2csb_ring_read_pos;
|
||||
U64 available_size = ctrl_state->u2csb_ring_size - unconsumed_size;
|
||||
if(available_size >= sizeof(thread))
|
||||
{
|
||||
good = 1;
|
||||
ctrl_state->u2csb_ring_write_pos += ring_write_struct(ctrl_state->u2csb_ring_base, ctrl_state->u2csb_ring_size, ctrl_state->u2csb_ring_write_pos, &thread);
|
||||
break;
|
||||
}
|
||||
if(os_now_microseconds() >= endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cond_var_wait(ctrl_state->u2csb_ring_cv, ctrl_state->u2csb_ring_mutex, endt_us);
|
||||
}
|
||||
if(good)
|
||||
{
|
||||
cond_var_broadcast(ctrl_state->u2csb_ring_cv);
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
internal void
|
||||
ctrl_u2csb_dequeue_req(CTRL_Handle *out_thread)
|
||||
{
|
||||
MutexScope(ctrl_state->u2csb_ring_mutex) for(;;)
|
||||
{
|
||||
U64 unconsumed_size = ctrl_state->u2csb_ring_write_pos - ctrl_state->u2csb_ring_read_pos;
|
||||
if(unconsumed_size >= sizeof(*out_thread))
|
||||
{
|
||||
ctrl_state->u2csb_ring_read_pos += ring_read_struct(ctrl_state->u2csb_ring_base, ctrl_state->u2csb_ring_size, ctrl_state->u2csb_ring_read_pos, out_thread);
|
||||
break;
|
||||
}
|
||||
cond_var_wait(ctrl_state->u2csb_ring_cv, ctrl_state->u2csb_ring_mutex, max_U64);
|
||||
}
|
||||
cond_var_broadcast(ctrl_state->u2csb_ring_cv);
|
||||
}
|
||||
|
||||
//- rjf: entry point
|
||||
|
||||
ASYNC_WORK_DEF(ctrl_call_stack_build_work)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
CTRL_CallStackCache *cache = &ctrl_state->call_stack_cache;
|
||||
|
||||
//- rjf: get next request & unpack
|
||||
CTRL_Handle thread_handle = {0};
|
||||
ctrl_u2csb_dequeue_req(&thread_handle);
|
||||
U64 hash = ctrl_hash_from_handle(thread_handle);
|
||||
U64 slot_idx = hash%cache->slots_count;
|
||||
U64 stripe_idx = hash%cache->stripes_count;
|
||||
CTRL_CallStackCacheSlot *slot = &cache->slots[slot_idx];
|
||||
CTRL_CallStackCacheStripe *stripe = &cache->stripes[stripe_idx];
|
||||
|
||||
//- rjf: produce mini entity context for just this process
|
||||
CTRL_EntityCtx *entity_ctx = push_array(scratch.arena, CTRL_EntityCtx, 1);
|
||||
MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex)
|
||||
{
|
||||
CTRL_EntityCtx *src_ctx = &ctrl_state->ctrl_thread_entity_store->ctx;
|
||||
CTRL_EntityCtx *dst_ctx = entity_ctx;
|
||||
{
|
||||
dst_ctx->root = &ctrl_entity_nil;
|
||||
dst_ctx->hash_slots_count = 1024;
|
||||
dst_ctx->hash_slots = push_array(scratch.arena, CTRL_EntityHashSlot, dst_ctx->hash_slots_count);
|
||||
MemoryCopyArray(dst_ctx->entity_kind_counts, src_ctx->entity_kind_counts);
|
||||
MemoryCopyArray(dst_ctx->entity_kind_alloc_gens, src_ctx->entity_kind_alloc_gens);
|
||||
}
|
||||
CTRL_Entity *src_thread = ctrl_entity_from_handle(src_ctx, thread_handle);
|
||||
CTRL_Entity *src_process = ctrl_process_from_entity(src_thread);
|
||||
{
|
||||
CTRL_EntityRec rec = {0};
|
||||
CTRL_Entity *dst_parent = &ctrl_entity_nil;
|
||||
for(CTRL_Entity *src_e = src_process; src_e != &ctrl_entity_nil; src_e = rec.next)
|
||||
{
|
||||
rec = ctrl_entity_rec_depth_first_pre(src_e, src_process);
|
||||
|
||||
// rjf: copy this entity
|
||||
CTRL_Entity *dst_e = push_array(scratch.arena, CTRL_Entity, 1);
|
||||
{
|
||||
dst_e->first = dst_e->last = dst_e->next = dst_e->prev = &ctrl_entity_nil;
|
||||
dst_e->parent = dst_parent;
|
||||
dst_e->kind = src_e->kind;
|
||||
dst_e->arch = src_e->arch;
|
||||
dst_e->is_frozen = src_e->is_frozen;
|
||||
dst_e->is_soloed = src_e->is_soloed;
|
||||
dst_e->rgba = src_e->rgba;
|
||||
dst_e->handle = src_e->handle;
|
||||
dst_e->id = src_e->id;
|
||||
dst_e->vaddr_range = src_e->vaddr_range;
|
||||
dst_e->stack_base = src_e->stack_base;
|
||||
dst_e->timestamp = src_e->timestamp;
|
||||
dst_e->bp_flags = src_e->bp_flags;
|
||||
dst_e->string = push_str8_copy(scratch.arena, src_e->string);
|
||||
}
|
||||
if(dst_parent == &ctrl_entity_nil)
|
||||
{
|
||||
dst_ctx->root = dst_e;
|
||||
}
|
||||
else
|
||||
{
|
||||
DLLPushBack_NPZ(&ctrl_entity_nil, dst_parent->first, dst_parent->last, dst_e, next, prev);
|
||||
}
|
||||
|
||||
// rjf: insert into hash map
|
||||
{
|
||||
U64 hash = ctrl_hash_from_handle(dst_e->handle);
|
||||
U64 slot_idx = hash%dst_ctx->hash_slots_count;
|
||||
CTRL_EntityHashSlot *slot = &dst_ctx->hash_slots[slot_idx];
|
||||
CTRL_EntityHashNode *node = 0;
|
||||
for(CTRL_EntityHashNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(ctrl_handle_match(n->entity->handle, dst_e->handle))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node == 0)
|
||||
{
|
||||
node = push_array(scratch.arena, CTRL_EntityHashNode, 1);
|
||||
MemoryZeroStruct(node);
|
||||
DLLPushBack(slot->first, slot->last, node);
|
||||
node->entity = dst_e;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: push/pop
|
||||
if(rec.push_count)
|
||||
{
|
||||
dst_parent = dst_e;
|
||||
}
|
||||
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
|
||||
{
|
||||
dst_parent = dst_parent->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: do task
|
||||
{
|
||||
CTRL_Entity *thread = ctrl_entity_from_handle(entity_ctx, thread_handle);
|
||||
CTRL_Entity *process = ctrl_process_from_entity(thread);
|
||||
|
||||
//- rjf: compute unwind to find list of all concrete frames, then
|
||||
// call stack, to determine list of all concrete & inline frames
|
||||
Arena *arena = arena_alloc();
|
||||
U64 pre_reg_gen = 0;
|
||||
U64 post_reg_gen = 0;
|
||||
U64 pre_mem_gen = 0;
|
||||
U64 post_mem_gen = 0;
|
||||
CTRL_Unwind unwind = {0};
|
||||
CTRL_CallStack call_stack = {0};
|
||||
{
|
||||
pre_reg_gen = ctrl_reg_gen();
|
||||
pre_mem_gen = ctrl_mem_gen();
|
||||
unwind = ctrl_unwind_from_thread(arena, entity_ctx, thread_handle, os_now_microseconds()+5000);
|
||||
call_stack = ctrl_call_stack_from_unwind(arena, process, &unwind);
|
||||
post_reg_gen = ctrl_reg_gen();
|
||||
post_mem_gen = ctrl_mem_gen();
|
||||
}
|
||||
|
||||
//- rjf: store new results in cache
|
||||
Arena *last_arena = arena;
|
||||
if(pre_reg_gen == post_reg_gen &&
|
||||
pre_mem_gen == post_mem_gen)
|
||||
{
|
||||
B32 found = 0;
|
||||
B32 committed = 0;
|
||||
MutexScopeW(stripe->rw_mutex) for(;;)
|
||||
{
|
||||
// rjf: try to find node & commit
|
||||
for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next)
|
||||
{
|
||||
if(ctrl_handle_match(n->thread, thread_handle))
|
||||
{
|
||||
found = 1;
|
||||
if(n->scope_touch_count == 0)
|
||||
{
|
||||
committed = 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;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: not found, or committed? -> abort
|
||||
if(!found || committed)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// rjf: found, not committed? -> wait & retry
|
||||
if(found && !committed)
|
||||
{
|
||||
cond_var_wait_rw_w(stripe->cv, stripe->rw_mutex, os_now_microseconds()+10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: release last results
|
||||
if(last_arena != 0)
|
||||
{
|
||||
arena_release(last_arena);
|
||||
}
|
||||
|
||||
//- rjf: mark work as done
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: broadcast update
|
||||
cond_var_broadcast(stripe->cv);
|
||||
if(ctrl_state->wakeup_hook != 0)
|
||||
{
|
||||
ctrl_state->wakeup_hook();
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Call Stack Tree Building Functions
|
||||
|
||||
ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
CTRL_Scope *ctrl_scope = ctrl_scope_open();
|
||||
|
||||
//- rjf: gather list of all thread handles
|
||||
U64 threads_count = 0;
|
||||
CTRL_Handle *threads = 0;
|
||||
CTRL_Handle *threads_processes = 0;
|
||||
Arch *threads_arches = 0;
|
||||
MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex)
|
||||
{
|
||||
CTRL_EntityCtx *ctx = &ctrl_state->ctrl_thread_entity_store->ctx;
|
||||
CTRL_EntityArray thread_entities = ctrl_entity_array_from_kind(ctx, CTRL_EntityKind_Thread);
|
||||
threads_count = thread_entities.count;
|
||||
threads = push_array(scratch.arena, CTRL_Handle, threads_count);
|
||||
threads_processes = push_array(scratch.arena, CTRL_Handle, threads_count);
|
||||
threads_arches = push_array(scratch.arena, Arch, threads_count);
|
||||
for EachIndex(idx, threads_count)
|
||||
{
|
||||
threads[idx] = thread_entities.v[idx]->handle;
|
||||
threads_processes[idx] = thread_entities.v[idx]->parent->handle;
|
||||
threads_arches[idx] = thread_entities.v[idx]->arch;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: gather all callstacks
|
||||
U64 pre_mem_gen = ctrl_mem_gen();
|
||||
U64 pre_reg_gen = ctrl_reg_gen();
|
||||
CTRL_CallStack *call_stacks = push_array(scratch.arena, CTRL_CallStack, threads_count);
|
||||
{
|
||||
for EachIndex(idx, threads_count)
|
||||
{
|
||||
call_stacks[idx] = ctrl_call_stack_from_thread(ctrl_scope, threads[idx], 0, os_now_microseconds()+1000);
|
||||
}
|
||||
}
|
||||
U64 post_mem_gen = ctrl_mem_gen();
|
||||
U64 post_reg_gen = ctrl_reg_gen();
|
||||
|
||||
//- rjf: build call stack tree
|
||||
Arena *arena = 0;
|
||||
CTRL_CallStackTree tree = {&ctrl_call_stack_tree_node_nil};
|
||||
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];
|
||||
CTRL_Handle process = threads_processes[thread_idx];
|
||||
Arch arch = threads_arches[thread_idx];
|
||||
CTRL_CallStack call_stack = call_stacks[thread_idx];
|
||||
CTRL_CallStackTreeNode *thread_node = tree.root;
|
||||
for EachIndex(frame_idx, call_stack.frames_count)
|
||||
{
|
||||
U64 vaddr = regs_rip_from_arch_block(arch, call_stack.frames[frame_idx].regs);
|
||||
U64 depth = call_stack.frames[frame_idx].inline_depth;
|
||||
CTRL_CallStackTreeNode *next_node = &ctrl_call_stack_tree_node_nil;
|
||||
for(CTRL_CallStackTreeNode *child = thread_node->first; child != &ctrl_call_stack_tree_node_nil; child = child->next)
|
||||
{
|
||||
if(ctrl_handle_match(child->process, process) && child->vaddr == vaddr && child->depth == depth)
|
||||
{
|
||||
next_node = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(next_node == &ctrl_call_stack_tree_node_nil)
|
||||
{
|
||||
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;
|
||||
}
|
||||
thread_node = next_node;
|
||||
}
|
||||
ctrl_handle_list_push(arena, &thread_node->threads, &thread);
|
||||
for(CTRL_CallStackTreeNode *n = thread_node; n != &ctrl_call_stack_tree_node_nil; n = n->parent)
|
||||
{
|
||||
n->all_descendant_threads_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: commit to cache
|
||||
Arena *old_arena = 0;
|
||||
CTRL_CallStackTreeCache *cache = &ctrl_state->call_stack_tree_cache;
|
||||
if(tree.root != &ctrl_call_stack_tree_node_nil)
|
||||
{
|
||||
MutexScopeW(cache->rw_mutex) for(;;)
|
||||
{
|
||||
if(cache->scope_touch_count == 0)
|
||||
{
|
||||
old_arena = cache->arena;
|
||||
cache->arena = arena;
|
||||
cache->tree = tree;
|
||||
cache->reg_gen = post_reg_gen;
|
||||
cache->mem_gen = post_mem_gen;
|
||||
cache->request_count -= 1;
|
||||
break;
|
||||
}
|
||||
cond_var_wait_rw_w(cache->cv, cache->rw_mutex, max_U64);
|
||||
}
|
||||
}
|
||||
cond_var_broadcast(cache->cv);
|
||||
|
||||
//- rjf: release old arena
|
||||
if(old_arena)
|
||||
{
|
||||
arena_release(old_arena);
|
||||
}
|
||||
|
||||
ctrl_scope_close(ctrl_scope);
|
||||
scratch_end(scratch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Memory Artifact Cache Hooks / Lookups
|
||||
|
||||
|
||||
@@ -646,53 +646,6 @@ struct CTRL_ThreadRegCache
|
||||
CTRL_ThreadRegCacheStripe *stripes;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Cache Types
|
||||
|
||||
typedef struct CTRL_CallStackCacheNode CTRL_CallStackCacheNode;
|
||||
struct CTRL_CallStackCacheNode
|
||||
{
|
||||
CTRL_CallStackCacheNode *next;
|
||||
CTRL_CallStackCacheNode *prev;
|
||||
|
||||
// rjf: key
|
||||
CTRL_Handle thread;
|
||||
U64 reg_gen;
|
||||
U64 mem_gen;
|
||||
|
||||
// rjf: counters
|
||||
U64 scope_touch_count;
|
||||
U64 working_count;
|
||||
|
||||
// rjf: value
|
||||
Arena *arena;
|
||||
CTRL_CallStack call_stack;
|
||||
};
|
||||
|
||||
typedef struct CTRL_CallStackCacheSlot CTRL_CallStackCacheSlot;
|
||||
struct CTRL_CallStackCacheSlot
|
||||
{
|
||||
CTRL_CallStackCacheNode *first;
|
||||
CTRL_CallStackCacheNode *last;
|
||||
};
|
||||
|
||||
typedef struct CTRL_CallStackCacheStripe CTRL_CallStackCacheStripe;
|
||||
struct CTRL_CallStackCacheStripe
|
||||
{
|
||||
Arena *arena;
|
||||
RWMutex rw_mutex;
|
||||
CondVar cv;
|
||||
};
|
||||
|
||||
typedef struct CTRL_CallStackCache CTRL_CallStackCache;
|
||||
struct CTRL_CallStackCache
|
||||
{
|
||||
U64 slots_count;
|
||||
CTRL_CallStackCacheSlot *slots;
|
||||
U64 stripes_count;
|
||||
CTRL_CallStackCacheStripe *stripes;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Module Image Info Cache Types
|
||||
|
||||
@@ -780,34 +733,6 @@ struct CTRL_EvalScope
|
||||
E_InterpretCtx interpret_ctx;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Control Cache Accessing Scopes
|
||||
|
||||
typedef struct CTRL_ScopeCallStackTouch CTRL_ScopeCallStackTouch;
|
||||
struct CTRL_ScopeCallStackTouch
|
||||
{
|
||||
CTRL_ScopeCallStackTouch *next;
|
||||
CTRL_CallStackCacheStripe *stripe;
|
||||
CTRL_CallStackCacheNode *node;
|
||||
};
|
||||
|
||||
typedef struct CTRL_Scope CTRL_Scope;
|
||||
struct CTRL_Scope
|
||||
{
|
||||
CTRL_Scope *next;
|
||||
CTRL_ScopeCallStackTouch *first_call_stack_touch;
|
||||
CTRL_ScopeCallStackTouch *last_call_stack_touch;
|
||||
U64 call_stack_tree_touch_count;
|
||||
};
|
||||
|
||||
typedef struct CTRL_TCTX CTRL_TCTX;
|
||||
struct CTRL_TCTX
|
||||
{
|
||||
Arena *arena;
|
||||
CTRL_Scope *free_scope;
|
||||
CTRL_ScopeCallStackTouch *free_call_stack_touch;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Module Requirement Cache Types
|
||||
|
||||
@@ -857,7 +782,6 @@ struct CTRL_State
|
||||
// rjf: caches
|
||||
CTRL_ProcessMemoryCache process_memory_cache;
|
||||
CTRL_ThreadRegCache thread_reg_cache;
|
||||
CTRL_CallStackCache call_stack_cache;
|
||||
CTRL_ModuleImageInfoCache module_image_info_cache;
|
||||
CTRL_CallStackTreeCache call_stack_tree_cache;
|
||||
|
||||
@@ -921,14 +845,6 @@ struct CTRL_State
|
||||
U64 u2ms_ring_read_pos;
|
||||
Mutex u2ms_ring_mutex;
|
||||
CondVar u2ms_ring_cv;
|
||||
|
||||
// rjf: user -> call stack builder ring buffer
|
||||
U64 u2csb_ring_size;
|
||||
U8 *u2csb_ring_base;
|
||||
U64 u2csb_ring_write_pos;
|
||||
U64 u2csb_ring_read_pos;
|
||||
Mutex u2csb_ring_mutex;
|
||||
CondVar u2csb_ring_cv;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -951,7 +867,6 @@ read_only global CTRL_CallStackTreeNode ctrl_call_stack_tree_node_nil =
|
||||
&ctrl_call_stack_tree_node_nil,
|
||||
&ctrl_call_stack_tree_node_nil,
|
||||
};
|
||||
thread_static CTRL_TCTX *ctrl_tctx = 0;
|
||||
thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0;
|
||||
|
||||
////////////////////////////////
|
||||
@@ -1078,13 +993,6 @@ internal CTRL_Entity *ctrl_thread_from_id(CTRL_EntityCtx *ctx, U64 id);
|
||||
//- rjf: applying events to entity caches
|
||||
internal void ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Accessing Scopes
|
||||
|
||||
internal CTRL_Scope *ctrl_scope_open(void);
|
||||
internal void ctrl_scope_close(CTRL_Scope *scope);
|
||||
internal void ctrl_scope_touch_call_stack_node__stripe_r_guarded(CTRL_Scope *scope, CTRL_CallStackCacheStripe *stripe, CTRL_CallStackCacheNode *node);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
@@ -1153,16 +1061,6 @@ internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityCtx *ctx,
|
||||
internal CTRL_CallStack ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *base_unwind);
|
||||
internal CTRL_CallStackFrame *ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U64 unwind_count, U64 inline_depth);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Cache Functions
|
||||
|
||||
internal CTRL_CallStack ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Tree Cache Functions
|
||||
|
||||
internal CTRL_CallStackTree ctrl_call_stack_tree(CTRL_Scope *scope, U64 endt_us);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Halting All Attached Processes
|
||||
|
||||
@@ -1235,21 +1133,6 @@ internal void ctrl_u2ms_dequeue_req(C_Key *out_key, CTRL_Handle *out_process, Rn
|
||||
//- rjf: entry point
|
||||
ASYNC_WORK_DEF(ctrl_mem_stream_work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Call Stack Building Functions
|
||||
|
||||
//- rjf: user -> memory stream communication
|
||||
internal B32 ctrl_u2csb_enqueue_req(CTRL_Handle thread, U64 endt_us);
|
||||
internal void ctrl_u2csb_dequeue_req(CTRL_Handle *out_thread);
|
||||
|
||||
//- rjf: entry point
|
||||
ASYNC_WORK_DEF(ctrl_call_stack_build_work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Call Stack Tree Building Functions
|
||||
|
||||
ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Memory Artifact Cache Hooks / Lookups
|
||||
|
||||
|
||||
@@ -11497,10 +11497,8 @@ rd_frame(void)
|
||||
//
|
||||
Access *frame_access_restore = rd_state->frame_access;
|
||||
DI_Scope *frame_di_scope_restore = rd_state->frame_di_scope;
|
||||
CTRL_Scope *frame_ctrl_scope_restore = rd_state->frame_ctrl_scope;
|
||||
rd_state->frame_access = access_open();
|
||||
rd_state->frame_di_scope = di_scope_open();
|
||||
rd_state->frame_ctrl_scope = ctrl_scope_open();
|
||||
rd_state->got_frame_call_stack_tree = 0;
|
||||
|
||||
//////////////////////////////
|
||||
@@ -17327,10 +17325,8 @@ rd_frame(void)
|
||||
//
|
||||
access_close(rd_state->frame_access);
|
||||
di_scope_close(rd_state->frame_di_scope);
|
||||
ctrl_scope_close(rd_state->frame_ctrl_scope);
|
||||
rd_state->frame_access = frame_access_restore;
|
||||
rd_state->frame_di_scope = frame_di_scope_restore;
|
||||
rd_state->frame_ctrl_scope = frame_ctrl_scope_restore;
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: submit rendering to all windows
|
||||
|
||||
@@ -601,7 +601,6 @@ struct RD_State
|
||||
F32 frame_dt;
|
||||
Access *frame_access;
|
||||
DI_Scope *frame_di_scope;
|
||||
CTRL_Scope *frame_ctrl_scope;
|
||||
CTRL_CallStackTree frame_call_stack_tree;
|
||||
B32 got_frame_call_stack_tree;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user