diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index 26b59be8..e2b494e4 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) { @@ -1451,6 +1467,14 @@ ctrl_scope_close(CTRL_Scope *scope) os_condition_variable_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) + { + os_condition_variable_broadcast(ctrl_state->call_stack_tree_cache.cv); + } SLLStackPush(ctrl_tctx->free_scope, scope); } @@ -1535,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(); @@ -3407,7 +3434,7 @@ ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U //~ rjf: Call Stack Cache Functions internal CTRL_CallStack -ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_Entity *thread, B32 high_priority, U64 endt_us) +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; @@ -3415,8 +3442,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ ////////////////////////////// //- rjf: unpack thread // - CTRL_Handle handle = thread->handle; - U64 hash = ctrl_hash_from_handle(handle); + 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]; @@ -3439,7 +3465,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ CTRL_CallStackCacheNode *node = 0; for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) { - if(ctrl_handle_match(n->thread, handle)) + if(ctrl_handle_match(n->thread, thread_handle)) { node = n; node_exists = 1; @@ -3464,8 +3490,8 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ } } - //- rjf: out of time => exit - if(retry_idx > 0 && os_now_microseconds() >= endt_us) + //- rjf: out of time, or got new result => exit + if((retry_idx > 0 && os_now_microseconds() >= endt_us) || !node_stale) { break; } @@ -3478,7 +3504,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ CTRL_CallStackCacheNode *node = 0; for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next) { - if(ctrl_handle_match(n->thread, handle)) + if(ctrl_handle_match(n->thread, thread_handle)) { node = n; break; @@ -3488,7 +3514,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ { node = push_array(stripe->arena, CTRL_CallStackCacheNode, 1); DLLPushBack(slot->first, slot->last, node); - node->thread = thread->handle; + node->thread = thread_handle; } if(node->working_count == 0) { @@ -3500,7 +3526,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ //- rjf: request if needed if(node_to_request != 0) { - if(ctrl_u2csb_enqueue_req(thread->handle, endt_us)) + 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); } @@ -3514,6 +3540,47 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_ 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; + OS_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)) + { + os_rw_mutex_drop_r(cache->rw_mutex); + async_push_work(ctrl_call_stack_tree_build_work); + os_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 + os_condition_variable_wait_rw_r(cache->cv, cache->rw_mutex, endt_us); + } + } + return result; +} + //////////////////////////////// //~ rjf: Halting All Attached Processes @@ -7261,3 +7328,133 @@ ASYNC_WORK_DEF(ctrl_call_stack_build_work) 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; + OS_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) + { + OS_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; + } + os_condition_variable_wait_rw_w(cache->cv, cache->rw_mutex, max_U64); + } + } + os_condition_variable_broadcast(cache->cv); + + //- rjf: release old arena + if(old_arena) + { + arena_release(old_arena); + } + + ctrl_scope_close(ctrl_scope); + scratch_end(scratch); + return 0; +} diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index d31a5e1b..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 @@ -271,6 +278,34 @@ struct CTRL_CallStack U64 concrete_frames_count; }; +//////////////////////////////// +//~ rjf: Call Stack Tree Types + +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; + CTRL_HandleList threads; + U64 all_descendant_threads_count; +}; + +typedef struct CTRL_CallStackTree CTRL_CallStackTree; +struct CTRL_CallStackTree +{ + CTRL_CallStackTreeNode *root; + U64 slots_count; + CTRL_CallStackTreeNode **slots; +}; + //////////////////////////////// //~ rjf: Trap Types @@ -700,6 +735,22 @@ struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCacheStripe *stripes; }; +//////////////////////////////// +//~ rjf: Call Stack Tree Cache Types + +typedef struct CTRL_CallStackTreeCache CTRL_CallStackTreeCache; +struct CTRL_CallStackTreeCache +{ + Arena *arena; + CTRL_CallStackTree tree; + OS_Handle cv; + OS_Handle rw_mutex; + U64 reg_gen; + U64 mem_gen; + U64 scope_touch_count; + U64 request_count; +}; + //////////////////////////////// //~ rjf: Touched Debug Info Directory Cache @@ -746,6 +797,7 @@ 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; @@ -791,6 +843,7 @@ struct CTRL_State CTRL_ThreadRegCache thread_reg_cache; CTRL_CallStackCache call_stack_cache; CTRL_ModuleImageInfoCache module_image_info_cache; + CTRL_CallStackTreeCache call_stack_tree_cache; // rjf: generations U64 run_gen; @@ -867,6 +920,14 @@ read_only global CTRL_Entity ctrl_entity_nil = &ctrl_entity_nil, &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, + &ctrl_call_stack_tree_node_nil, +}; thread_static CTRL_TCTX *ctrl_tctx = 0; thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0; @@ -896,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); @@ -1071,7 +1133,12 @@ internal CTRL_CallStackFrame *ctrl_call_stack_frame_from_unwind_and_inline_depth //////////////////////////////// //~ rjf: Call Stack Cache Functions -internal CTRL_CallStack ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_Entity *thread, B32 high_priority, U64 endt_us); +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 @@ -1155,4 +1222,9 @@ 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); + #endif // CTRL_CORE_H diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index ad61e676..980f99ae 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -1209,7 +1209,7 @@ d_query_cached_rip_from_thread_unwind(CTRL_Entity *thread, U64 unwind_count) else { CTRL_Scope *ctrl_scope = ctrl_scope_open(); - CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, 0); + CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, 0); if(callstack.concrete_frames_count != 0) { result = regs_rip_from_arch_block(thread->arch, callstack.concrete_frames[unwind_count%callstack.concrete_frames_count]->regs); @@ -1889,7 +1889,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P CTRL_Scope *ctrl_scope = ctrl_scope_open(); // rjf: thread => call stack - CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000); + CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, os_now_microseconds()+10000); // rjf: use first unwind frame to generate trap if(callstack.concrete_frames_count > 1) diff --git a/src/eval/eval_ir.c b/src/eval/eval_ir.c index a551bba4..e9efeb63 100644 --- a/src/eval/eval_ir.c +++ b/src/eval/eval_ir.c @@ -752,9 +752,9 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_push_irtree_and_type_from_expr(arena, parent, &e_default_identifier_resolution_rule, disallow_autohooks, 1, r_expr); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); - E_TypeKey r_type = e_type_key_unwrap(r_tree.type_key, E_TypeUnwrapFlag_AllDecorative); + E_TypeKey r_type = e_type_key_unwrap(r_tree.type_key, E_TypeUnwrapFlag_AllDecorative & ~E_TypeUnwrapFlag_Enums); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); - E_TypeKey r_type_direct = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_All); + E_TypeKey r_type_direct = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_All & ~E_TypeUnwrapFlag_Enums); U64 r_type_direct_size = e_type_byte_size_from_key(r_type_direct); // rjf: bad conditions? -> error if applicable, exit @@ -812,7 +812,7 @@ e_push_irtree_and_type_from_expr(Arena *arena, E_IRTreeAndType *root_parent, E_I E_IRTreeAndType r_tree = e_push_irtree_and_type_from_expr(arena, parent, &e_default_identifier_resolution_rule, disallow_autohooks, 1, r_expr); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); E_TypeKey r_type = r_tree.type_key; - E_TypeKey r_type_unwrapped = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_AllDecorative); + E_TypeKey r_type_unwrapped = e_type_key_unwrap(r_type, E_TypeUnwrapFlag_AllDecorative & (~E_TypeUnwrapFlag_Enums)); E_TypeKind r_type_unwrapped_kind = e_type_kind_from_key(r_type_unwrapped); // rjf: bad conditions? -> error if applicable, exit diff --git a/src/os/core/linux/os_core_linux.c b/src/os/core/linux/os_core_linux.c index d8ded967..c61fa270 100644 --- a/src/os/core/linux/os_core_linux.c +++ b/src/os/core/linux/os_core_linux.c @@ -1091,14 +1091,18 @@ internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name) { OS_Handle result = {0}; - if (name.size > 0) { + if (name.size > 0) + { // TODO: we need to allocate shared memory to store sem_t // NotImplemented; - } else { + } + else + { sem_t *s = mmap(0, sizeof(*s), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); AssertAlways(s != MAP_FAILED); int err = sem_init(s, 0, initial_count); - if (err == 0) { + if(err == 0) + { result.u64[0] = (U64)s; } } @@ -1127,17 +1131,19 @@ os_semaphore_close(OS_Handle semaphore) internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us) { + // TODO(rjf): we need to use `sem_timedwait` here. AssertAlways(endt_us == max_U64); - for (;;) { + for(;;) + { int err = sem_wait((sem_t*)semaphore.u64[0]); - if (err == 0) { + if(err == 0) + { break; - } else { - if (errno == EAGAIN) { - continue; - } } - InvalidPath; + else if(errno == EAGAIN) + { + continue; + } break; } return 1; @@ -1146,20 +1152,50 @@ os_semaphore_take(OS_Handle semaphore, U64 endt_us) internal void os_semaphore_drop(OS_Handle semaphore) { - for (;;) { + for(;;) + { int err = sem_post((sem_t*)semaphore.u64[0]); - if (err == 0) { + if(err == 0) + { break; - } else { - if (errno == EAGAIN) { + } + else + { + if(errno == EAGAIN) + { continue; } } - InvalidPath; break; } } +//- rjf: barriers + +internal OS_Handle +os_barrier_alloc(U64 count) +{ + OS_LNX_Entity *entity = os_w32_entity_alloc(OS_LNX_EntityKind_Barrier); + pthread_barrier_init(&entity->barrier, count); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_barrier_release(OS_Handle barrier) +{ + OS_LNX_Entity *entity = (OS_LNX_Entity*)PtrFromInt(barrier.u64[0]); + pthread_barrier_destroy(&entity->barrier); + os_lnx_entity_release(entity); +} + +internal void +os_barrier_wait(OS_Handle barrier) +{ + OS_LNX_Entity *entity = (OS_LNX_Entity*)PtrFromInt(barrier.u64[0]); + pthread_barrier_wait(&entity->barrier); +} + //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) diff --git a/src/os/core/linux/os_core_linux.h b/src/os/core/linux/os_core_linux.h index 1770d239..4b345a4b 100644 --- a/src/os/core/linux/os_core_linux.h +++ b/src/os/core/linux/os_core_linux.h @@ -67,6 +67,7 @@ typedef enum OS_LNX_EntityKind OS_LNX_EntityKind_Mutex, OS_LNX_EntityKind_RWMutex, OS_LNX_EntityKind_ConditionVariable, + OS_LNX_EntityKind_Barrier, } OS_LNX_EntityKind; @@ -90,6 +91,7 @@ struct OS_LNX_Entity pthread_cond_t cond_handle; pthread_mutex_t rwlock_mutex_handle; } cv; + pthread_barrier_t barrier; }; }; diff --git a/src/os/core/os_core.h b/src/os/core/os_core.h index 415bb662..158c5185 100644 --- a/src/os/core/os_core.h +++ b/src/os/core/os_core.h @@ -299,7 +299,12 @@ internal void os_semaphore_release(OS_Handle semaphore); internal OS_Handle os_semaphore_open(String8 name); internal void os_semaphore_close(OS_Handle semaphore); internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us); -internal void os_semaphore_drop(OS_Handle semaphore); +internal void os_semaphore_drop(OS_Handle semaphore); + +//- rjf: barriers +internal OS_Handle os_barrier_alloc(U64 count); +internal void os_barrier_release(OS_Handle barrier); +internal void os_barrier_wait(OS_Handle barrier); //- rjf: scope macros #define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex)) diff --git a/src/os/core/win32/os_core_win32.c b/src/os/core/win32/os_core_win32.c index 65d7926d..175ad4f5 100644 --- a/src/os/core/win32/os_core_win32.c +++ b/src/os/core/win32/os_core_win32.c @@ -1352,6 +1352,32 @@ os_semaphore_drop(OS_Handle semaphore) ReleaseSemaphore(handle, 1, 0); } +//- rjf: barriers + +internal OS_Handle +os_barrier_alloc(U64 count) +{ + OS_W32_Entity *entity = os_w32_entity_alloc(OS_W32_EntityKind_Barrier); + InitializeSynchronizationBarrier(&entity->sb, count, -1); + OS_Handle result = {IntFromPtr(entity)}; + return result; +} + +internal void +os_barrier_release(OS_Handle barrier) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(barrier.u64[0]); + DeleteSynchronizationBarrier(&entity->sb); + os_w32_entity_release(entity); +} + +internal void +os_barrier_wait(OS_Handle barrier) +{ + OS_W32_Entity *entity = (OS_W32_Entity*)PtrFromInt(barrier.u64[0]); + EnterSynchronizationBarrier(&entity->sb, 0); +} + //////////////////////////////// //~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS) diff --git a/src/os/core/win32/os_core_win32.h b/src/os/core/win32/os_core_win32.h index 22a21f80..4938a828 100644 --- a/src/os/core/win32/os_core_win32.h +++ b/src/os/core/win32/os_core_win32.h @@ -46,6 +46,7 @@ typedef enum OS_W32_EntityKind OS_W32_EntityKind_Mutex, OS_W32_EntityKind_RWMutex, OS_W32_EntityKind_ConditionVariable, + OS_W32_EntityKind_Barrier, } OS_W32_EntityKind; @@ -66,6 +67,7 @@ struct OS_W32_Entity CRITICAL_SECTION mutex; SRWLOCK rw_mutex; CONDITION_VARIABLE cv; + SYNCHRONIZATION_BARRIER sb; }; }; diff --git a/src/raddbg/generated/raddbg.meta.c b/src/raddbg/generated/raddbg.meta.c index 0be16561..59dbf0e3 100644 --- a/src/raddbg/generated/raddbg.meta.c +++ b/src/raddbg/generated/raddbg.meta.c @@ -424,7 +424,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[24] = {str8_lit_comp("window"), str8_lit_comp("x:\n{\n //- rjf: text rasterization settings\n @default(1) @display_name('Smooth UI Text') @description(\"Controls whether or not UI text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_ui_text': bool,\n @default(1) @display_name('Hint UI Text') @description(\"Controls whether or not UI text is hinted, for better text readability at small sizes.\")\n 'hint_ui_text': bool,\n @default(0) @display_name('Smooth Code Text') @description(\"Controls whether or not code text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_code_text': bool,\n @default(1) @display_name('Hint Code Text') @description(\"Controls whether or not code text is hinted, for better text readability at small sizes.\")\n 'hint_code_text': bool,\n @default(11) @display_name('Window Font Size') @description(\"Controls the window's default font size. Does not apply to tabs with their own font size set.\")\n 'font_size': @range[6, 72] u64,\n\n //- rjf: size settings\n @default(3.f) @display_name('Window Row Height') @description(\"Controls the window's default row height, in multiples of the font size. Does not apply to tabs with their own row height set.\")\n 'row_height': @range[1.75f, 5.f] f32,\n @default(3.f) @description(\"Controls the height of tabs, in multiples of the font size.\")\n 'tab_height': @range[1.75f, 5.f] f32,\n\n //- rjf: theme settings\n @default(1) @display_name('Use Project Theme') @description(\"Prefer using the project theme for this window, if any. If off, only the user's theme settings will be used.\")\n 'use_project_theme': bool,\n}\n")}, {str8_lit_comp("tab"), str8_lit_comp("@row_commands(@file copy_tab_full_path, @file show_file_in_explorer, duplicate_tab, close_tab)\nx:\n{\n @override @display_name('Tab Font Size') @description(\"Controls the tab's font size.\") @no_callee_helper\n 'font_size': @range[6, 72] u64,\n}\n")}, {str8_lit_comp("watch"), str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watches': query,\n}\n")}, -{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change':bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, +{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(1) @display_name('Line Wrapping') @description(\"Splits textual lines into multiple visual lines, so that all text is within the visible area.\")\n 'line_wrapping': bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change': bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")}, {str8_lit_comp("disasm"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n 'arch': code_string,\n 'syntax': code_string,\n 'size': expr_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n\n}\n")}, {str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n}\n")}, {str8_lit_comp("bitmap"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as a bitmap.\")\n 'expression': expr_string,\n @description(\"An expression describing the width of the bitmap, in pixels.\") @order(0) 'w': u64,\n @description(\"An expression describing the height of the bitmap, in pixels.\") @order(1) 'h': u64,\n @display_name(\"Bitmap Format\") @description(\"The pixel format that the bitmap data should be interpreted as being within.\")\n 'fmt': code_string,\n}\n")}, diff --git a/src/raddbg/raddbg.mdesk b/src/raddbg/raddbg.mdesk index 0e0d27c2..7248a67a 100644 --- a/src/raddbg/raddbg.mdesk +++ b/src/raddbg/raddbg.mdesk @@ -481,8 +481,10 @@ RD_VocabTable: 'lang': code_string, @default(1) @description("Controls whether or not line numbers are shown.") 'show_line_numbers':bool, + @no_callee_helper @default(1) @display_name('Line Wrapping') @description("Splits textual lines into multiple visual lines, so that all text is within the visible area.") + 'line_wrapping': bool, @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description("Scrolls to the bottom if the text is changed.") - 'scroll_to_bottom_on_change':bool, + 'scroll_to_bottom_on_change': bool, @no_callee_helper @no_revert @default(0) @display_name('Transient') @description("Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.") 'auto': bool, } diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 08186ae4..6d5f3b6f 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1817,7 +1817,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range) case CTRL_EntityKind_Thread: { CTRL_Scope *ctrl_scope = ctrl_scope_open(); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, 1, rd_state->frame_eval_memread_endt_us); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, 1, rd_state->frame_eval_memread_endt_us); U64 concrete_frame_idx = e_interpret_ctx->reg_unwind_count; if(concrete_frame_idx < call_stack.concrete_frames_count) { @@ -6469,7 +6469,7 @@ rd_window_frame(void) Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol")); CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process); B32 call_stack_high_priority = ctrl_handle_match(ctrl_entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, ctrl_entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, ctrl_entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); if(call_stack.frames_count != 0) { ui_spacer(ui_em(1.5f, 1.f)); @@ -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,28 @@ 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 +#if 0 + { + 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); + } +#endif + //- rjf: add macro / lookup rules for unattached processes { String8 collection_name = str8_lit("unattached_processes"); @@ -16231,7 +16254,7 @@ rd_frame(void) { 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_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, 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) { @@ -16250,7 +16273,7 @@ rd_frame(void) { 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_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, 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) 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 20d9e521..c1795d34 100644 --- a/src/raddbg/raddbg_eval.c +++ b/src/raddbg/raddbg_eval.c @@ -968,7 +968,7 @@ E_TYPE_IREXT_FUNCTION_DEF(call_stack) B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); accel->arch = entity->arch; accel->process = ctrl_process_from_entity(entity)->handle; - accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); } scratch_end(scratch); } @@ -1538,6 +1538,95 @@ 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); +} + //////////////////////////////// //~ rjf: Debug Info Tables Eval Hooks diff --git a/src/raddbg/raddbg_eval.h b/src/raddbg/raddbg_eval.h index b5980a03..b2269be9 100644 --- a/src/raddbg/raddbg_eval.h +++ b/src/raddbg/raddbg_eval.h @@ -100,6 +100,13 @@ E_TYPE_ACCESS_FUNCTION_DEF(ctrl_entities); E_TYPE_EXPAND_INFO_FUNCTION_DEF(ctrl_entities); 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); + //////////////////////////////// //~ rjf: Debug Info Tables Eval Hooks diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index 1379a785..73b687d9 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -104,6 +104,21 @@ rd_code_view_build(Arena *arena, RD_CodeViewState *cv, RD_CodeViewBuildFlags fla } } + ////////////////////////////// + //- rjf: set up wrap cache + // + if(cv->wrap_arena == 0) + { + cv->wrap_arena = rd_push_view_arena(); + } + if(cv->wrap_total_vline_count == 0) + { + arena_clear(cv->wrap_arena); + cv->wrap_total_vline_count = text_info->lines_count; + cv->wrap_cache_slots_count = text_info->lines_count/64; + cv->wrap_cache_slots = push_array(cv->wrap_arena, RD_CodeViewTLineWrapCacheSlot, cv->wrap_cache_slots_count); + } + ////////////////////////////// //- rjf: determine visible line range / count // @@ -1015,7 +1030,7 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row) info.callstack_thread = entity; U64 frame_num = ev_block_num_from_id(block, key.child_id); B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); if(1 <= frame_num && frame_num <= call_stack.frames_count) { CTRL_CallStackFrame *f = &call_stack.frames[frame_num-1]; @@ -2924,7 +2939,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory) CTRL_Scope *ctrl_scope = ctrl_scope_open(); CTRL_Entity *selected_thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_regs()->thread); CTRL_Entity *selected_process = ctrl_entity_ancestor_from_kind(selected_thread, CTRL_EntityKind_Process); - CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, selected_thread, 1, 0); + CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(ctrl_scope, selected_thread->handle, 1, 0); CTRL_Entity *eval_process = &ctrl_entity_nil; if(eval.space.kind == RD_EvalSpaceKind_CtrlEntity) { diff --git a/src/raddbg/raddbg_views.h b/src/raddbg/raddbg_views.h index 3b6d5c2a..f13b2ecc 100644 --- a/src/raddbg/raddbg_views.h +++ b/src/raddbg/raddbg_views.h @@ -14,6 +14,30 @@ enum RD_CodeViewBuildFlag_All = 0xffffffff, }; +typedef struct RD_CodeViewTLineSplitNode RD_CodeViewTLineSplitNode; +struct RD_CodeViewTLineSplitNode +{ + RD_CodeViewTLineSplitNode *next; + U64 off; +}; + +typedef struct RD_CodeViewTLineWrapCacheNode RD_CodeViewTLineWrapCacheNode; +struct RD_CodeViewTLineWrapCacheNode +{ + RD_CodeViewTLineWrapCacheNode *hash_next; + RD_CodeViewTLineWrapCacheNode *hash_prev; + S64 line_num; + RD_CodeViewTLineSplitNode *first_split; + RD_CodeViewTLineSplitNode *last_split; +}; + +typedef struct RD_CodeViewTLineWrapCacheSlot RD_CodeViewTLineWrapCacheSlot; +struct RD_CodeViewTLineWrapCacheSlot +{ + RD_CodeViewTLineWrapCacheNode *first; + RD_CodeViewTLineWrapCacheNode *last; +}; + typedef struct RD_CodeViewState RD_CodeViewState; struct RD_CodeViewState { @@ -32,6 +56,12 @@ struct RD_CodeViewState Arena *find_text_arena; String8 find_text_fwd; String8 find_text_bwd; + + // rjf: line wrapping cache & info + Arena *wrap_arena; + RD_CodeViewTLineWrapCacheSlot *wrap_cache_slots; + U64 wrap_cache_slots_count; + U64 wrap_total_vline_count; }; typedef struct RD_CodeViewBuildResult RD_CodeViewBuildResult; diff --git a/src/raddbg/raddbg_widgets.c b/src/raddbg/raddbg_widgets.c index d1da7715..64ea699e 100644 --- a/src/raddbg/raddbg_widgets.c +++ b/src/raddbg/raddbg_widgets.c @@ -552,7 +552,7 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process); Arch arch = entity->arch; B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread); - CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); + CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0); B32 did_first_known = 0; for(U64 idx = 0, limit = 10; idx < call_stack.frames_count && idx < limit; diff --git a/src/text_cache/text_cache.c b/src/text_cache/text_cache.c index c142a844..59fdec01 100644 --- a/src/text_cache/text_cache.c +++ b/src/text_cache/text_cache.c @@ -2132,7 +2132,7 @@ txt_scope_node_from_info_off(TXT_TextInfo *info, U64 off) scope_n != &txt_scope_node_nil; scope_n = txt_scope_node_from_info_num(info, scope_n->parent_num)) { - if(off < info->tokens.v[scope_n->token_idx_range.max].range.max) + if(info->tokens.v[scope_n->token_idx_range.min].range.min <= off && off < info->tokens.v[scope_n->token_idx_range.max].range.max) { result = scope_n; break;