This commit is contained in:
Ryan Fleury
2025-03-25 16:21:51 -07:00
107 changed files with 25560 additions and 24723 deletions
+428 -309
View File
@@ -43,6 +43,21 @@ ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind)
return cause;
}
internal CTRL_ExceptionKind
ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind)
{
CTRL_ExceptionKind result = CTRL_ExceptionKind_Null;
switch(kind)
{
default:{}break;
case DMN_ExceptionKind_MemoryRead: {result = CTRL_ExceptionKind_MemoryRead;}break;
case DMN_ExceptionKind_MemoryWrite: {result = CTRL_ExceptionKind_MemoryWrite;}break;
case DMN_ExceptionKind_MemoryExecute: {result = CTRL_ExceptionKind_MemoryExecute;}break;
case DMN_ExceptionKind_CppThrow: {result = CTRL_ExceptionKind_CppThrow;}break;
}
return result;
}
internal String8
ctrl_string_from_event_kind(CTRL_EventKind kind)
{
@@ -91,6 +106,21 @@ ctrl_string_from_msg_kind(CTRL_MsgKind kind)
return result;
}
internal CTRL_EntityKind
ctrl_entity_kind_from_string(String8 string)
{
CTRL_EntityKind result = CTRL_EntityKind_Null;
for EachNonZeroEnumVal(CTRL_EntityKind, k)
{
if(str8_match(ctrl_entity_kind_code_name_table[k], string, 0))
{
result = k;
break;
}
}
return result;
}
////////////////////////////////
//~ rjf: Machine/Handle Pair Type Functions
@@ -136,6 +166,37 @@ ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src)
return dst;
}
internal String8
ctrl_string_from_handle(Arena *arena, CTRL_Handle handle)
{
String8 result = push_str8f(arena, "$0x%I64x$0x%I64x", handle.machine_id, handle.dmn_handle.u64[0]);
return result;
}
internal CTRL_Handle
ctrl_handle_from_string(String8 string)
{
CTRL_Handle handle = {0};
{
Temp scratch = scratch_begin(0, 0);
U8 split = '$';
String8List parts = str8_split(scratch.arena, string, &split, 1, 0);
if(parts.first && parts.first->next)
{
CTRL_MachineID machine_id = 0;
DMN_Handle dmn_handle = {0};
if(try_u64_from_str8_c_rules(parts.first->string, &machine_id) &&
try_u64_from_str8_c_rules(parts.first->next->string, &dmn_handle.u64[0]))
{
handle.machine_id = machine_id;
handle.dmn_handle = dmn_handle;
}
}
scratch_end(scratch);
}
return handle;
}
////////////////////////////////
//~ rjf: Trap Type Functions
@@ -201,7 +262,6 @@ ctrl_msg_deep_copy(Arena *arena, CTRL_Msg *dst, CTRL_Msg *src)
dst->env_string_list = str8_list_copy(arena, &src->env_string_list);
dst->traps = ctrl_trap_list_copy(arena, &src->traps);
dst->user_bps = ctrl_user_breakpoint_list_copy(arena, &src->user_bps);
dst->meta_evals = *deep_copy_from_struct(arena, CTRL_MetaEvalArray, &src->meta_evals);
}
//- rjf: list building
@@ -332,10 +392,6 @@ ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs)
str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->condition.size);
str8_serial_push_data(scratch.arena, &msgs_srlzed, bp->condition.str, bp->condition.size);
}
// rjf: write meta-eval-info array
String8 meta_evals_srlzed = serialized_from_struct(scratch.arena, CTRL_MetaEvalArray, &msg->meta_evals);
str8_serial_push_string(scratch.arena, &msgs_srlzed, meta_evals_srlzed);
}
}
String8 string = str8_serial_end(arena, &msgs_srlzed);
@@ -459,12 +515,6 @@ ctrl_msg_list_from_serialized_string(Arena *arena, String8 string)
bp->condition.str = push_array_no_zero(arena, U8, bp->condition.size);
read_off += str8_deserial_read(string, read_off, bp->condition.str, bp->condition.size, 1);
}
// rjf: read meta-eval-info array
String8 meta_evals_data = str8_skip(string, read_off);
U64 meta_evals_size = 0;
msg->meta_evals = *struct_from_serialized(arena, CTRL_MetaEvalArray, meta_evals_data, .advance_out = &meta_evals_size);
read_off += meta_evals_size;
}
}
return msgs;
@@ -639,95 +689,100 @@ ctrl_entity_store_release(CTRL_EntityStore *cache)
//- rjf: string allocation/deletion
internal U64
ctrl_name_bucket_idx_from_string_size(U64 size)
ctrl_name_bucket_num_from_string_size(U64 size)
{
U64 size_rounded = u64_up_to_pow2(size+1);
size_rounded = ClampBot((1<<4), size_rounded);
U64 bucket_idx = 0;
switch(size_rounded)
U64 bucket_num = 0;
if(size > 0)
{
case 1<<4: {bucket_idx = 0;}break;
case 1<<5: {bucket_idx = 1;}break;
case 1<<6: {bucket_idx = 2;}break;
case 1<<7: {bucket_idx = 3;}break;
case 1<<8: {bucket_idx = 4;}break;
case 1<<9: {bucket_idx = 5;}break;
case 1<<10:{bucket_idx = 6;}break;
default:{bucket_idx = ArrayCount(((CTRL_EntityStore *)0)->free_string_chunks)-1;}break;
for EachElement(idx, ctrl_entity_string_bucket_chunk_sizes)
{
if(size <= ctrl_entity_string_bucket_chunk_sizes[idx])
{
bucket_num = idx+1;
break;
}
}
}
return bucket_idx;
return bucket_num;
}
internal String8
ctrl_entity_string_alloc(CTRL_EntityStore *store, String8 string)
{
if(string.size == 0) {return str8_zero();}
U64 bucket_idx = ctrl_name_bucket_idx_from_string_size(string.size);
CTRL_EntityStringChunkNode *node = store->free_string_chunks[bucket_idx];
// rjf: pull from bucket free list
if(node != 0)
//- rjf: allocate node
CTRL_EntityStringChunkNode *node = 0;
{
if(bucket_idx == ArrayCount(store->free_string_chunks)-1)
U64 bucket_num = ctrl_name_bucket_num_from_string_size(string.size);
if(bucket_num == ArrayCount(ctrl_entity_string_bucket_chunk_sizes))
{
node = 0;
CTRL_EntityStringChunkNode *prev = 0;
for(CTRL_EntityStringChunkNode *n = store->free_string_chunks[bucket_idx];
n != 0;
prev = n, n = n->next)
CTRL_EntityStringChunkNode *best_node = 0;
CTRL_EntityStringChunkNode *best_node_prev = 0;
U64 best_node_size = max_U64;
{
if(n->size >= string.size)
for(CTRL_EntityStringChunkNode *n = store->free_string_chunks[bucket_num-1], *prev = 0; n != 0; (prev = n, n = n->next))
{
if(prev == 0)
if(n->size >= string.size && n->size < best_node_size)
{
store->free_string_chunks[bucket_idx] = n->next;
best_node = n;
best_node_prev = prev;
best_node_size = n->size;
}
else
{
prev->next = n->next;
}
node = n;
break;
}
}
if(best_node != 0)
{
node = best_node;
if(best_node_prev)
{
best_node_prev->next = best_node->next;
}
else
{
store->free_string_chunks[bucket_num-1] = best_node->next;
}
}
else
{
U64 chunk_size = u64_up_to_pow2(string.size);
node = (CTRL_EntityStringChunkNode *)push_array(store->arena, U8, chunk_size);
}
}
else
else if(bucket_num != 0)
{
SLLStackPop(store->free_string_chunks[bucket_idx]);
node = store->free_string_chunks[bucket_num-1];
if(node != 0)
{
SLLStackPop(store->free_string_chunks[bucket_num-1]);
}
else
{
node = (CTRL_EntityStringChunkNode *)push_array(store->arena, U8, ctrl_entity_string_bucket_chunk_sizes[bucket_num-1]);
}
}
}
// rjf: no found node -> allocate new
if(node == 0)
//- rjf: fill node
String8 result = {0};
if(node != 0)
{
U64 chunk_size = 0;
if(bucket_idx < ArrayCount(store->free_string_chunks)-1)
{
chunk_size = 1<<(bucket_idx+4);
}
else
{
chunk_size = u64_up_to_pow2(string.size);
}
U8 *chunk_memory = push_array(store->arena, U8, chunk_size);
node = (CTRL_EntityStringChunkNode *)chunk_memory;
node->size = chunk_size;
result.str = (U8 *)node;
result.size = string.size;
MemoryCopy(result.str, string.str, result.size);
}
// rjf: fill string & return
String8 allocated_string = str8((U8 *)node, string.size);
MemoryCopy((U8 *)node, string.str, string.size);
return allocated_string;
return result;
}
internal void
ctrl_entity_string_release(CTRL_EntityStore *store, String8 string)
{
if(string.size == 0) {return;}
U64 bucket_idx = ctrl_name_bucket_idx_from_string_size(string.size);
CTRL_EntityStringChunkNode *node = (CTRL_EntityStringChunkNode *)string.str;
node->size = u64_up_to_pow2(string.size);
SLLStackPush(store->free_string_chunks[bucket_idx], node);
U64 bucket_num = ctrl_name_bucket_num_from_string_size(string.size);
if(1 <= bucket_num && bucket_num <= ArrayCount(rd_name_bucket_chunk_sizes))
{
U64 bucket_idx = bucket_num-1;
CTRL_EntityStringChunkNode *node = (CTRL_EntityStringChunkNode *)string.str;
SLLStackPush(store->free_string_chunks[bucket_idx], node);
node->size = u64_up_to_pow2(string.size);
}
}
//- rjf: entity construction/deletion
@@ -1305,7 +1360,6 @@ ctrl_init(void)
ctrl_state->ctrl_thread_entity_store = ctrl_entity_store_alloc();
ctrl_state->dmn_event_arena = arena_alloc();
ctrl_state->user_entry_point_arena = arena_alloc();
ctrl_state->user_meta_eval_arena = arena_alloc();
ctrl_state->dbg_dir_arena = arena_alloc();
for(CTRL_ExceptionCodeKind k = (CTRL_ExceptionCodeKind)0; k < CTRL_ExceptionCodeKind_COUNT; k = (CTRL_ExceptionCodeKind)(k+1))
{
@@ -1370,6 +1424,7 @@ ctrl_stored_hash_from_process_vaddr_range(CTRL_Handle process, Rng1U64 range, B3
//- rjf: try to read from cache
B32 is_good = 0;
B32 process_node_exists = 0;
B32 is_stale = 1;
OS_MutexScopeR(process_stripe->rw_mutex)
{
@@ -1377,6 +1432,7 @@ ctrl_stored_hash_from_process_vaddr_range(CTRL_Handle process, Rng1U64 range, B3
{
if(ctrl_handle_match(n->handle, process))
{
process_node_exists = 1;
U64 range_slot_idx = range_hash%n->range_hash_slots_count;
CTRL_ProcessMemoryRangeHashSlot *range_slot = &n->range_hash_slots[range_slot_idx];
for(CTRL_ProcessMemoryRangeHashNode *range_n = range_slot->first; range_n != 0; range_n = range_n->next)
@@ -1395,7 +1451,7 @@ ctrl_stored_hash_from_process_vaddr_range(CTRL_Handle process, Rng1U64 range, B3
}
//- rjf: not good -> create process cache node if necessary
if(!is_good)
if(!is_good && !process_node_exists)
{
OS_MutexScopeW(process_stripe->rw_mutex)
{
@@ -2994,44 +3050,118 @@ ctrl_unwind_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_Handle threa
internal CTRL_CallStack
ctrl_call_stack_from_unwind(Arena *arena, DI_Scope *di_scope, CTRL_Entity *process, CTRL_Unwind *base_unwind)
{
Temp scratch = scratch_begin(&arena, 1);
Arch arch = process->arch;
CTRL_CallStack result = {0};
result.concrete_frame_count = base_unwind->frames.count;
result.total_frame_count = result.concrete_frame_count;
result.frames = push_array(arena, CTRL_CallStackFrame, result.concrete_frame_count);
for(U64 idx = 0; idx < result.concrete_frame_count; idx += 1)
{
CTRL_UnwindFrame *src = &base_unwind->frames.v[idx];
CTRL_CallStackFrame *dst = &result.frames[idx];
U64 rip_vaddr = regs_rip_from_arch_block(arch, src->regs);
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr);
DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0);
RDI_Scope *scope = rdi_scope_from_voff(rdi, rip_voff);
// rjf: fill concrete frame info
dst->regs = src->regs;
dst->rdi = rdi;
dst->procedure = rdi_element_from_name_idx(rdi, Procedures, scope->proc_idx);
// rjf: push inline frames
for(RDI_Scope *s = scope;
s->inline_site_idx != 0;
s = rdi_element_from_name_idx(rdi, Scopes, s->parent_scope_idx))
typedef struct FrameNode FrameNode;
struct FrameNode
{
RDI_InlineSite *site = rdi_element_from_name_idx(rdi, InlineSites, s->inline_site_idx);
CTRL_CallStackInlineFrame *inline_frame = push_array(arena, CTRL_CallStackInlineFrame, 1);
DLLPushFront(dst->first_inline_frame, dst->last_inline_frame, inline_frame);
inline_frame->inline_site = site;
dst->inline_frame_count += 1;
result.inline_frame_count += 1;
result.total_frame_count += 1;
FrameNode *next;
CTRL_CallStackFrame v;
};
//- rjf: gather all frames
FrameNode *first_frame = 0;
FrameNode *last_frame = 0;
U64 frame_count = 0;
for(U64 base_frame_idx = 0; base_frame_idx < base_unwind->frames.count; base_frame_idx += 1)
{
// rjf: unpack
CTRL_UnwindFrame *src = &base_unwind->frames.v[base_frame_idx];
U64 rip_vaddr = regs_rip_from_arch_block(arch, src->regs);
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, rip_vaddr);
U64 rip_voff = ctrl_voff_from_vaddr(module, rip_vaddr);
DI_Key dbgi_key = ctrl_dbgi_key_from_module(module);
RDI_Parsed *rdi = di_rdi_from_key(di_scope, &dbgi_key, 0);
RDI_Scope *scope = rdi_scope_from_voff(rdi, rip_voff);
// rjf: build inline frames (minus parent & inline depth)
FrameNode *first_inline_frame = 0;
FrameNode *last_inline_frame = 0;
U64 inline_frame_count = 0;
for(RDI_Scope *s = scope;
s->inline_site_idx != 0;
s = rdi_element_from_name_idx(rdi, Scopes, s->parent_scope_idx))
{
FrameNode *dst_inline = push_array(scratch.arena, FrameNode, 1);
if(first_inline_frame == 0)
{
first_inline_frame = dst_inline;
}
last_inline_frame = dst_inline;
SLLQueuePush(first_frame, last_frame, dst_inline);
dst_inline->v.unwind_count = base_frame_idx;
dst_inline->v.regs = src->regs;
dst_inline->v.rdi = rdi;
dst_inline->v.inline_site = rdi_element_from_name_idx(rdi, InlineSites, s->inline_site_idx);
frame_count += 1;
inline_frame_count += 1;
}
// rjf: build concrete frame
FrameNode *dst_base = push_array(scratch.arena, FrameNode, 1);
SLLQueuePush(first_frame, last_frame, dst_base);
dst_base->v.unwind_count = base_frame_idx;
dst_base->v.regs = src->regs;
dst_base->v.rdi = rdi;
dst_base->v.procedure = rdi_element_from_name_idx(rdi, Procedures, scope->proc_idx);
frame_count += 1;
// rjf: hook up inline frames to point to concrete frame, and to account for inline depth
U64 inline_frame_idx = 0;
for(FrameNode *inline_frame = first_inline_frame; inline_frame != 0; inline_frame = inline_frame->next, inline_frame_idx += 1)
{
inline_frame->v.parent_num = frame_count;
inline_frame->v.inline_depth = inline_frame_count - inline_frame_idx;
if(inline_frame == last_inline_frame)
{
break;
}
}
}
//- rjf: package
result.count = frame_count;
result.frames = push_array(arena, CTRL_CallStackFrame, result.count);
{
U64 idx = 0;
for(FrameNode *n = first_frame; n != 0; n = n->next, idx += 1)
{
MemoryCopyStruct(&result.frames[idx], &n->v);
}
}
}
scratch_end(scratch);
return result;
}
internal CTRL_CallStackFrame *
ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U64 unwind_count, U64 inline_depth)
{
CTRL_CallStackFrame *f = 0;
{
U64 base_frame_idx = 0;
for(U64 idx = 0; idx < call_stack->count; idx += 1)
{
if(call_stack->frames[idx].parent_num == 0)
{
if(base_frame_idx == unwind_count)
{
f = &call_stack->frames[idx];
break;
}
base_frame_idx += 1;
}
}
if(f != 0 && call_stack->frames + inline_depth < f)
{
f -= inline_depth;
}
}
return f;
}
////////////////////////////////
//~ rjf: Halting All Attached Processes
@@ -3242,8 +3372,6 @@ ctrl_thread__entry_point(void *p)
//- rjf: unpack per-message parameterizations & store
{
MemoryCopyArray(ctrl_state->exception_code_filters, msg->exception_code_filters);
arena_clear(ctrl_state->user_meta_eval_arena);
ctrl_state->user_meta_evals = *deep_copy_from_struct(ctrl_state->user_meta_eval_arena, CTRL_MetaEvalArray, &msg->meta_evals);
}
//- rjf: process message
@@ -3331,11 +3459,11 @@ ctrl_thread__entry_point(void *p)
//- rjf: breakpoint resolution
internal void
ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_Handle process, CTRL_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out)
ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_EvalScope *eval_scope, CTRL_Handle process, CTRL_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out)
{
if(user_bps->first == 0) { return; }
Temp scratch = scratch_begin(&arena, 1);
DI_Scope *di_scope = di_scope_open();
DI_Scope *di_scope = eval_scope->di_scope;
CTRL_Entity *module_entity = ctrl_entity_from_handle(ctrl_state->ctrl_thread_entity_store, module);
CTRL_Entity *debug_info_path_entity = ctrl_entity_child_from_kind(module_entity, CTRL_EntityKind_DebugInfoPath);
DI_Key dbgi_key = {debug_info_path_entity->string, debug_info_path_entity->timestamp};
@@ -3399,45 +3527,37 @@ ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_Handle proc
}
}break;
//- rjf: symbol:voff-based breakpoints
case CTRL_UserBreakpointKind_SymbolNameAndOffset:
//- rjf: expression-based breakpoints
case CTRL_UserBreakpointKind_Expression:
{
String8 symbol_name = bp->string;
U64 voff = bp->u64;
RDI_NameMap *mapptr = rdi_element_from_name_idx(rdi, NameMaps, RDI_NameMapKind_Procedures);
RDI_ParsedNameMap map = {0};
rdi_parsed_from_name_map(rdi, mapptr, &map);
RDI_NameMapNode *node = rdi_name_map_lookup(rdi, &map, symbol_name.str, symbol_name.size);
if(node != 0)
String8 expr = bp->string;
E_Value value = e_value_from_string(expr);
if(value.u64 != 0)
{
U32 id_count = 0;
U32 *ids = rdi_matches_from_map_node(rdi, node, &id_count);
for(U32 match_i = 0; match_i < id_count; match_i += 1)
{
RDI_Procedure *procedure = rdi_element_from_name_idx(rdi, Procedures, ids[match_i]);
U64 proc_voff = rdi_first_voff_from_procedure(rdi, procedure);
U64 proc_vaddr = proc_voff + base_vaddr;
DMN_Trap trap = {process.dmn_handle, proc_vaddr + voff, (U64)bp};
dmn_trap_chunk_list_push(arena, traps_out, 256, &trap);
}
DMN_Trap trap = {process.dmn_handle, value.u64, (U64)bp};
dmn_trap_chunk_list_push(arena, traps_out, 256, &trap);
}
}break;
}
}
di_scope_close(di_scope);
scratch_end(scratch);
}
internal void
ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out)
ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_EvalScope *eval_scope, CTRL_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out)
{
for(CTRL_UserBreakpointNode *n = user_bps->first; n != 0; n = n->next)
{
CTRL_UserBreakpoint *bp = &n->v;
if(bp->kind == CTRL_UserBreakpointKind_VirtualAddress)
if(bp->kind == CTRL_UserBreakpointKind_Expression)
{
DMN_Trap trap = {process.dmn_handle, bp->u64, (U64)bp};
dmn_trap_chunk_list_push(arena, traps_out, 256, &trap);
String8 expr = bp->string;
E_Value value = e_value_from_string(expr);
if(value.u64 != 0)
{
DMN_Trap trap = {process.dmn_handle, value.u64, (U64)bp};
dmn_trap_chunk_list_push(arena, traps_out, 256, &trap);
}
}
}
}
@@ -4404,42 +4524,134 @@ ctrl_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range)
//- rjf: meta evaluations
case CTRL_EvalSpaceKind_Meta:
{
Temp scratch = scratch_begin(0, 0);
U64 meta_eval_idx = space.u64s[0];
if(meta_eval_idx < ctrl_state->user_meta_evals.count)
{
CTRL_MetaEval *meval = &ctrl_state->user_meta_evals.v[meta_eval_idx];
// rjf: copy meta evaluation to scratch arena, to form range of legal reads
arena_push(scratch.arena, 0, 64);
String8 meval_srlzed = serialized_from_struct(scratch.arena, CTRL_MetaEval, meval);
U64 pos_min = arena_pos(scratch.arena);
CTRL_MetaEval *meval_read = struct_from_serialized(scratch.arena, CTRL_MetaEval, meval_srlzed);
U64 pos_opl = arena_pos(scratch.arena);
// rjf: rebase all pointer values in meta evaluation to be relative to base pointer
struct_rebase_ptrs(CTRL_MetaEval, meval_read, meval_read);
// rjf: perform actual read
Rng1U64 legal_range = r1u64(0, pos_opl-pos_min);
if(contains_1u64(legal_range, range.min))
{
result = 1;
U64 range_dim = dim_1u64(range);
U64 bytes_to_read = Min(range_dim, (legal_range.max - range.min));
MemoryCopy(out, ((U8 *)meval_read) + range.min, bytes_to_read);
if(bytes_to_read < range_dim)
{
MemoryZero((U8 *)out + bytes_to_read, range_dim - bytes_to_read);
}
}
}
scratch_end(scratch);
}break;
}
return result;
}
//- rjf: control thread eval scopes
internal CTRL_EvalScope *
ctrl_thread__eval_scope_begin(Arena *arena, CTRL_Entity *thread)
{
CTRL_EvalScope *scope = push_array(arena, CTRL_EvalScope, 1);
scope->di_scope = di_scope_open();
// rjf: unpack thread
Arch arch = thread->arch;
U64 thread_rip_vaddr = dmn_rip_from_thread(thread->handle.dmn_handle);
CTRL_Entity *process = ctrl_process_from_entity(thread);
CTRL_Entity *module = ctrl_module_from_process_vaddr(process, thread_rip_vaddr);
U64 thread_rip_voff = ctrl_voff_from_vaddr(module, thread_rip_vaddr);
// rjf: gather evaluation modules
U64 eval_modules_count = Max(1, ctrl_state->ctrl_thread_entity_store->entity_kind_counts[CTRL_EntityKind_Module]);
E_Module *eval_modules = push_array(arena, E_Module, eval_modules_count);
E_Module *eval_modules_primary = &eval_modules[0];
eval_modules_primary->rdi = &di_rdi_parsed_nil;
eval_modules_primary->vaddr_range = r1u64(0, max_U64);
{
U64 eval_module_idx = 0;
for(CTRL_Entity *machine = ctrl_state->ctrl_thread_entity_store->root->first;
machine != &ctrl_entity_nil;
machine = machine->next)
{
if(machine->kind != CTRL_EntityKind_Machine) { continue; }
for(CTRL_Entity *process = machine->first;
process != &ctrl_entity_nil;
process = process->next)
{
if(process->kind != CTRL_EntityKind_Process) { continue; }
for(CTRL_Entity *mod = process->first;
mod != &ctrl_entity_nil;
mod = mod->next)
{
if(mod->kind != CTRL_EntityKind_Module) { continue; }
CTRL_Entity *dbg_path = ctrl_entity_child_from_kind(mod, CTRL_EntityKind_DebugInfoPath);
DI_Key dbgi_key = {dbg_path->string, dbg_path->timestamp};
eval_modules[eval_module_idx].arch = arch;
eval_modules[eval_module_idx].rdi = di_rdi_from_key(scope->di_scope, &dbgi_key, max_U64);
eval_modules[eval_module_idx].vaddr_range = mod->vaddr_range;
eval_modules[eval_module_idx].space = e_space_make(CTRL_EvalSpaceKind_Entity);
eval_modules[eval_module_idx].space.u64_0 = (U64)process;
if(mod == module)
{
eval_modules_primary = &eval_modules[eval_module_idx];
}
eval_module_idx += 1;
}
}
}
}
// rjf: build eval type context
{
E_TypeCtx *ctx = &scope->type_ctx;
ctx->ip_vaddr = thread_rip_vaddr;
ctx->ip_voff = thread_rip_voff;
ctx->modules = eval_modules;
ctx->modules_count = eval_modules_count;
ctx->primary_module = eval_modules_primary;
}
e_select_type_ctx(&scope->type_ctx);
// rjf: build eval parse context
ProfScope("build eval parse context")
{
E_ParseCtx *ctx = &scope->parse_ctx;
ctx->ip_vaddr = thread_rip_vaddr;
ctx->ip_voff = thread_rip_voff;
ctx->ip_thread_space = e_space_make(CTRL_EvalSpaceKind_Entity);
ctx->ip_thread_space.u64_0 = (U64)thread;
ctx->modules = eval_modules;
ctx->modules_count = eval_modules_count;
ctx->primary_module = eval_modules_primary;
ctx->regs_map = ctrl_string2reg_from_arch(arch);
ctx->reg_alias_map = ctrl_string2alias_from_arch(arch);
ctx->locals_map = e_push_locals_map_from_rdi_voff(arena, eval_modules_primary->rdi, thread_rip_voff);
ctx->member_map = e_push_member_map_from_rdi_voff(arena, eval_modules_primary->rdi, thread_rip_voff);
}
e_select_parse_ctx(&scope->parse_ctx);
// rjf: build eval IR context
{
E_IRCtx *ctx = &scope->ir_ctx;
ctx->macro_map = push_array(arena, E_String2ExprMap, 1);
ctx->macro_map[0] = e_string2expr_map_make(arena, 512);
ctx->lookup_rule_map = push_array(arena, E_LookupRuleMap, 1);
ctx->lookup_rule_map[0] = e_lookup_rule_map_make(arena, 512);
ctx->irgen_rule_map = push_array(arena, E_IRGenRuleMap, 1);
ctx->irgen_rule_map[0] = e_irgen_rule_map_make(arena, 512);
ctx->auto_hook_map = push_array(arena, E_AutoHookMap, 1);
ctx->auto_hook_map[0] = e_auto_hook_map_make(arena, 512);
}
e_select_ir_ctx(&scope->ir_ctx);
// rjf: build eval interpretation context
{
E_InterpretCtx *ctx = &scope->interpret_ctx;
ctx->space_rw_user_data = ctrl_state->ctrl_thread_entity_store;
ctx->space_read = ctrl_eval_space_read;
ctx->primary_space = eval_modules_primary->space;
ctx->reg_arch = eval_modules_primary->arch;
ctx->reg_space = e_space_make(CTRL_EvalSpaceKind_Entity);
ctx->reg_space.u64_0 = (U64)thread;
ctx->module_base = push_array(arena, U64, 1);
ctx->module_base[0]= module->vaddr_range.min;
ctx->tls_base = push_array(arena, U64, 1);
}
e_select_interpret_ctx(&scope->interpret_ctx);
return scope;
}
internal void
ctrl_thread__eval_scope_end(CTRL_EvalScope *scope)
{
di_scope_close(scope->di_scope);
}
//- rjf: log flusher
internal void
@@ -4771,25 +4983,30 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
//- rjf: gather all initial breakpoints
//
DMN_TrapChunkList user_traps = {0};
for(CTRL_Entity *machine = ctrl_state->ctrl_thread_entity_store->root->first;
machine != &ctrl_entity_nil;
machine = machine->next)
{
if(machine->kind != CTRL_EntityKind_Machine) { continue; }
for(CTRL_Entity *process = machine->first; process != &ctrl_entity_nil; process = process->next)
CTRL_Entity *thread = ctrl_entity_from_handle(ctrl_state->ctrl_thread_entity_store, target_thread);
CTRL_EvalScope *eval_scope = ctrl_thread__eval_scope_begin(scratch.arena, thread);
for(CTRL_Entity *machine = ctrl_state->ctrl_thread_entity_store->root->first;
machine != &ctrl_entity_nil;
machine = machine->next)
{
if(process->kind != CTRL_EntityKind_Process) { continue; }
// rjf: resolve module-dependent user bps
for(CTRL_Entity *module = process->first; module != &ctrl_entity_nil; module = module->next)
if(machine->kind != CTRL_EntityKind_Machine) { continue; }
for(CTRL_Entity *process = machine->first; process != &ctrl_entity_nil; process = process->next)
{
if(module->kind != CTRL_EntityKind_Module) { continue; }
ctrl_thread__append_resolved_module_user_bp_traps(scratch.arena, process->handle, module->handle, &msg->user_bps, &user_traps);
if(process->kind != CTRL_EntityKind_Process) { continue; }
// rjf: resolve module-dependent user bps
for(CTRL_Entity *module = process->first; module != &ctrl_entity_nil; module = module->next)
{
if(module->kind != CTRL_EntityKind_Module) { continue; }
ctrl_thread__append_resolved_module_user_bp_traps(scratch.arena, eval_scope, process->handle, module->handle, &msg->user_bps, &user_traps);
}
// rjf: push virtual-address user breakpoints per-process
ctrl_thread__append_resolved_process_user_bp_traps(scratch.arena, eval_scope, process->handle, &msg->user_bps, &user_traps);
}
// rjf: push virtual-address user breakpoints per-process
ctrl_thread__append_resolved_process_user_bp_traps(scratch.arena, process->handle, &msg->user_bps, &user_traps);
}
ctrl_thread__eval_scope_end(eval_scope);
}
//////////////////////////////
@@ -5053,39 +5270,48 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
}break;
case DMN_EventKind_CreateProcess:
{
DMN_TrapChunkList new_traps = {0};
ctrl_thread__append_resolved_process_user_bp_traps(scratch.arena, ctrl_handle_make(CTRL_MachineID_Local, event->process), &msg->user_bps, &new_traps);
log_infof("step_rule: create_process -> resolve traps\n");
log_infof("new_traps:\n{\n");
for(DMN_TrapChunkNode *n = new_traps.first; n != 0; n = n->next)
CTRL_EvalScope *eval_scope = ctrl_thread__eval_scope_begin(scratch.arena, &ctrl_entity_nil);
{
for(U64 idx = 0; idx < n->count; idx += 1)
DMN_TrapChunkList new_traps = {0};
ctrl_thread__append_resolved_process_user_bp_traps(scratch.arena, eval_scope, ctrl_handle_make(CTRL_MachineID_Local, event->process), &msg->user_bps, &new_traps);
log_infof("step_rule: create_process -> resolve traps\n");
log_infof("new_traps:\n{\n");
for(DMN_TrapChunkNode *n = new_traps.first; n != 0; n = n->next)
{
DMN_Trap *trap = &n->v[idx];
log_infof("{process:[0x%I64x], vaddr:0x%I64x}\n", trap->process.u64[0], trap->vaddr);
for(U64 idx = 0; idx < n->count; idx += 1)
{
DMN_Trap *trap = &n->v[idx];
log_infof("{process:[0x%I64x], vaddr:0x%I64x}\n", trap->process.u64[0], trap->vaddr);
}
}
log_infof("}\n\n");
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &new_traps);
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &user_traps, &new_traps);
}
log_infof("}\n\n");
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &new_traps);
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &user_traps, &new_traps);
ctrl_thread__eval_scope_end(eval_scope);
}break;
case DMN_EventKind_LoadModule:
{
DMN_TrapChunkList new_traps = {0};
ctrl_thread__append_resolved_module_user_bp_traps(scratch.arena, ctrl_handle_make(CTRL_MachineID_Local, event->process), ctrl_handle_make(CTRL_MachineID_Local, event->module), &msg->user_bps, &new_traps);
log_infof("step_rule: load_module -> resolve traps\n");
log_infof("new_traps:\n{\n");
for(DMN_TrapChunkNode *n = new_traps.first; n != 0; n = n->next)
CTRL_Entity *thread = ctrl_entity_from_handle(ctrl_state->ctrl_thread_entity_store, ctrl_handle_make(CTRL_MachineID_Local, event->thread));
CTRL_EvalScope *eval_scope = ctrl_thread__eval_scope_begin(scratch.arena, thread);
{
for(U64 idx = 0; idx < n->count; idx += 1)
DMN_TrapChunkList new_traps = {0};
ctrl_thread__append_resolved_module_user_bp_traps(scratch.arena, eval_scope, ctrl_handle_make(CTRL_MachineID_Local, event->process), ctrl_handle_make(CTRL_MachineID_Local, event->module), &msg->user_bps, &new_traps);
log_infof("step_rule: load_module -> resolve traps\n");
log_infof("new_traps:\n{\n");
for(DMN_TrapChunkNode *n = new_traps.first; n != 0; n = n->next)
{
DMN_Trap *trap = &n->v[idx];
log_infof("{process:[0x%I64x], vaddr:0x%I64x}\n", trap->process.u64[0], trap->vaddr);
for(U64 idx = 0; idx < n->count; idx += 1)
{
DMN_Trap *trap = &n->v[idx];
log_infof("{process:[0x%I64x], vaddr:0x%I64x}\n", trap->process.u64[0], trap->vaddr);
}
}
log_infof("}\n\n");
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &new_traps);
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &user_traps, &new_traps);
}
log_infof("}\n\n");
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &joined_traps, &new_traps);
dmn_trap_chunk_list_concat_shallow_copy(scratch.arena, &user_traps, &new_traps);
ctrl_thread__eval_scope_end(eval_scope);
}break;
}
@@ -5406,118 +5632,9 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
// rjf: evaluate hit stop conditions
if(conditions.node_count != 0) ProfScope("evaluate hit stop conditions")
{
DI_Scope *di_scope = di_scope_open();
// rjf: gather evaluation modules
U64 eval_modules_count = Max(1, ctrl_state->ctrl_thread_entity_store->entity_kind_counts[CTRL_EntityKind_Module]);
E_Module *eval_modules = push_array(temp.arena, E_Module, eval_modules_count);
E_Module *eval_modules_primary = &eval_modules[0];
eval_modules_primary->rdi = &di_rdi_parsed_nil;
eval_modules_primary->vaddr_range = r1u64(0, max_U64);
{
U64 eval_module_idx = 0;
for(CTRL_Entity *machine = ctrl_state->ctrl_thread_entity_store->root->first;
machine != &ctrl_entity_nil;
machine = machine->next)
{
if(machine->kind != CTRL_EntityKind_Machine) { continue; }
for(CTRL_Entity *process = machine->first;
process != &ctrl_entity_nil;
process = process->next)
{
if(process->kind != CTRL_EntityKind_Process) { continue; }
for(CTRL_Entity *mod = process->first;
mod != &ctrl_entity_nil;
mod = mod->next)
{
if(mod->kind != CTRL_EntityKind_Module) { continue; }
CTRL_Entity *dbg_path = ctrl_entity_child_from_kind(mod, CTRL_EntityKind_DebugInfoPath);
DI_Key dbgi_key = {dbg_path->string, dbg_path->timestamp};
eval_modules[eval_module_idx].arch = arch;
eval_modules[eval_module_idx].rdi = di_rdi_from_key(di_scope, &dbgi_key, max_U64);
eval_modules[eval_module_idx].vaddr_range = mod->vaddr_range;
eval_modules[eval_module_idx].space = e_space_make(CTRL_EvalSpaceKind_Entity);
eval_modules[eval_module_idx].space.u64_0 = (U64)process;
if(mod == module)
{
eval_modules_primary = &eval_modules[eval_module_idx];
}
eval_module_idx += 1;
}
}
}
}
// rjf: loop through all conditions, check all
CTRL_EvalScope *eval_scope = ctrl_thread__eval_scope_begin(temp.arena, thread);
for(String8Node *condition_n = conditions.first; condition_n != 0; condition_n = condition_n->next)
{
// rjf: build eval type context
E_TypeCtx type_ctx = zero_struct;
{
E_TypeCtx *ctx = &type_ctx;
ctx->ip_vaddr = thread_rip_vaddr;
ctx->ip_voff = thread_rip_voff;
ctx->modules = eval_modules;
ctx->modules_count = eval_modules_count;
ctx->primary_module = eval_modules_primary;
}
e_select_type_ctx(&type_ctx);
// rjf: build eval parse context
E_ParseCtx parse_ctx = zero_struct;
ProfScope("build eval parse context")
{
E_ParseCtx *ctx = &parse_ctx;
ctx->ip_vaddr = thread_rip_vaddr;
ctx->ip_voff = thread_rip_voff;
ctx->ip_thread_space = e_space_make(CTRL_EvalSpaceKind_Entity);
ctx->ip_thread_space.u64_0 = (U64)thread;
ctx->modules = eval_modules;
ctx->modules_count = eval_modules_count;
ctx->primary_module = eval_modules_primary;
ctx->regs_map = ctrl_string2reg_from_arch(arch);
ctx->reg_alias_map = ctrl_string2alias_from_arch(arch);
ctx->locals_map = e_push_locals_map_from_rdi_voff(temp.arena, eval_modules_primary->rdi, thread_rip_voff);
ctx->member_map = e_push_member_map_from_rdi_voff(temp.arena, eval_modules_primary->rdi, thread_rip_voff);
}
e_select_parse_ctx(&parse_ctx);
// rjf: build eval IR context
E_IRCtx ir_ctx = zero_struct;
{
E_IRCtx *ctx = &ir_ctx;
ctx->macro_map = push_array(temp.arena, E_String2ExprMap, 1);
ctx->macro_map[0] = e_string2expr_map_make(temp.arena, 512);
E_TypeKey meval_type_key = e_type_key_cons_base(type(CTRL_MetaEval));
for EachIndex(idx, ctrl_state->user_meta_evals.count)
{
E_Space space = e_space_make(CTRL_EvalSpaceKind_Meta);
E_Expr *expr = e_push_expr(scratch.arena, E_ExprKind_LeafOffset, 0);
expr->space = space;
expr->mode = E_Mode_Offset;
expr->type_key = meval_type_key;
e_string2expr_map_insert(temp.arena, ctx->macro_map, ctrl_state->user_meta_evals.v[idx].label, expr);
}
}
e_select_ir_ctx(&ir_ctx);
// rjf: build eval interpretation context
E_InterpretCtx interpret_ctx = zero_struct;
{
E_InterpretCtx *ctx = &interpret_ctx;
ctx->space_rw_user_data = ctrl_state->ctrl_thread_entity_store;
ctx->space_read = ctrl_eval_space_read;
ctx->primary_space = eval_modules_primary->space;
ctx->reg_arch = eval_modules_primary->arch;
ctx->reg_space = e_space_make(CTRL_EvalSpaceKind_Entity);
ctx->reg_space.u64_0 = (U64)thread;
ctx->module_base = push_array(temp.arena, U64, 1);
ctx->module_base[0]= module->vaddr_range.min;
ctx->frame_base = push_array(temp.arena, U64, 1);
ctx->tls_base = push_array(temp.arena, U64, 1);
}
e_select_interpret_ctx(&interpret_ctx, type_ctx.primary_module->rdi, type_ctx.ip_voff);
// rjf: evaluate
E_Eval eval = zero_struct;
ProfScope("evaluate expression")
@@ -5540,7 +5657,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
break;
}
}
di_scope_close(di_scope);
ctrl_thread__eval_scope_end(eval_scope);
}
// rjf: gather trap net hits
@@ -5848,6 +5965,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
event->entity = ctrl_handle_make(CTRL_MachineID_Local, stop_event->thread);
event->parent = ctrl_handle_make(CTRL_MachineID_Local, stop_event->process);
event->exception_code = stop_event->code;
event->exception_kind = ctrl_exception_kind_from_dmn(stop_event->exception_kind);
event->vaddr_rng = r1u64(stop_event->address, stop_event->address);
event->rip_vaddr = stop_event->instruction_pointer;
ctrl_c2u_push_events(&evts);
@@ -5922,6 +6040,7 @@ ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
event->entity = ctrl_handle_make(CTRL_MachineID_Local, stop_event->thread);
event->parent = ctrl_handle_make(CTRL_MachineID_Local, stop_event->process);
event->exception_code = stop_event->code;
event->exception_kind = ctrl_exception_kind_from_dmn(stop_event->exception_kind);
event->vaddr_rng = r1u64(stop_event->address, stop_event->address);
event->rip_vaddr = stop_event->instruction_pointer;
}