diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index f969fc848..c870e7732 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -1056,6 +1056,21 @@ ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle return result; } +internal CTRL_Handle +ctrl_thread_snapshot(CTRL_MachineID machine_id, CTRL_Handle thread) +{ + DEMON_Handle handle = ctrl_demon_handle_from_ctrl(thread); + DEMON_Handle snapshot_handle = demon_snapshot_thread(handle); + return ctrl_handle_from_demon(snapshot_handle); +} + +internal void +ctrl_snapshot_release(CTRL_MachineID machine_id, CTRL_Handle process) +{ + DEMON_Handle handle = ctrl_demon_handle_from_ctrl(process); + demon_snapshot_release(handle); +} + //- rjf: register reading/writing internal void * diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 6947bde7f..f78573b67 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -564,6 +564,8 @@ internal String8 ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, C internal String8 ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, U64 limit, U64 endt_us); internal B32 ctrl_process_write_data(CTRL_MachineID machine_id, CTRL_Handle process, U64 vaddr, String8 data); internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range, B32 zero_terminated); +internal CTRL_Handle ctrl_process_snapshot(CTRL_MachineID machine_id, CTRL_Handle thread); +internal void ctrl_snapshot_release(CTRL_MachineID machine_id, CTRL_Handle snapshot); //- rjf: register reading/writing internal void *ctrl_reg_block_from_thread(CTRL_MachineID machine_id, CTRL_Handle thread); diff --git a/src/demon/demon_common.h b/src/demon/demon_common.h index 4375f0794..b60bbe58c 100644 --- a/src/demon/demon_common.h +++ b/src/demon/demon_common.h @@ -15,6 +15,7 @@ typedef enum DEMON_EntityKind DEMON_EntityKind_Process, DEMON_EntityKind_Thread, DEMON_EntityKind_Module, + DEMON_EntityKind_Snapshot, DEMON_EntityKind_COUNT } diff --git a/src/demon/demon_core.c b/src/demon/demon_core.c index 43077629d..e4f8b6f6d 100644 --- a/src/demon/demon_core.c +++ b/src/demon/demon_core.c @@ -396,6 +396,35 @@ demon_detach_process(DEMON_Handle process){ return(result); } +internal DEMON_Handle +demon_snapshot_thread(DEMON_Handle thread) +{ + DEMON_Handle result = 0; + if (demon_access_begin()){ + DEMON_Entity *entity = demon_ent_ptr_from_handle(thread); + if (entity != 0 && + entity->kind == DEMON_EntityKind_Thread){ + result = demon_os_create_snapshot(entity); + } + demon_access_end(); + } + + return(result); +} + +internal void +demon_snapshot_release(DEMON_Handle snapshot) +{ + if (demon_access_begin()){ + DEMON_Entity *entity = demon_ent_ptr_from_handle(snapshot); + if (entity != 0 && + entity->kind == DEMON_EntityKind_Snapshot){ + demon_os_snapshot_release(entity); + } + demon_access_end(); + } +} + //////////////////////////////// //~ rjf: Entity Functions @@ -646,7 +675,7 @@ demon_read_memory(DEMON_Handle process, void *dst, U64 src_address, U64 size){ if (demon_access_begin()){ DEMON_Entity *entity = demon_ent_ptr_from_handle(process); if (entity != 0 && - entity->kind == DEMON_EntityKind_Process){ + (entity->kind == DEMON_EntityKind_Process || entity->kind == DEMON_EntityKind_Snapshot)){ bytes_read = demon_os_read_memory(entity, dst, src_address, size); } demon_access_end(); diff --git a/src/demon/demon_core.h b/src/demon/demon_core.h index f164fcd1e..3f55a64e1 100644 --- a/src/demon/demon_core.h +++ b/src/demon/demon_core.h @@ -240,6 +240,8 @@ internal U32 demon_launch_process(OS_LaunchOptions *options); internal B32 demon_attach_process(U32 pid); internal B32 demon_kill_process(DEMON_Handle process, U32 exit_code); internal B32 demon_detach_process(DEMON_Handle process); +internal DEMON_Handle demon_snapshot_thread(DEMON_Handle process); +internal void demon_snapshot_release(DEMON_Handle snapshot); //////////////////////////////// //~ rjf: Entity Functions diff --git a/src/demon/demon_os.h b/src/demon/demon_os.h index 1b0bc5933..cdf142c7b 100644 --- a/src/demon/demon_os.h +++ b/src/demon/demon_os.h @@ -54,6 +54,9 @@ internal B32 demon_os_attach_process(U32 pid); internal B32 demon_os_kill_process(DEMON_Entity *process, U32 exit_code); internal B32 demon_os_detach_process(DEMON_Entity *process); +internal DEMON_Handle demon_os_create_snapshot(DEMON_Entity *thread); +internal void demon_os_snapshot_release(DEMON_Entity *entity); + //////////////////////////////// //~ rjf: @demon_os_hooks Entity Functions diff --git a/src/demon/linux/demon_os_linux.c b/src/demon/linux/demon_os_linux.c index 0c92f461f..bf5d83c54 100644 --- a/src/demon/linux/demon_os_linux.c +++ b/src/demon/linux/demon_os_linux.c @@ -1757,6 +1757,20 @@ demon_os_detach_process(DEMON_Entity *process){ return(0); } +internal DEMON_Handle +demon_os_create_snapshot(DEMON_Entity *thread) +{ + NotImplemented; + return(0); +} + +internal void +demon_os_snapshot_release(DEMON_Entity *entity) +{ + NotImplemented; + return(0); +} + //////////////////////////////// //~ rjf: @demon_os_hooks Entity Functions diff --git a/src/demon/win32/demon_os_win32.c b/src/demon/win32/demon_os_win32.c index d55c00b19..87f00b5a6 100644 --- a/src/demon/win32/demon_os_win32.c +++ b/src/demon/win32/demon_os_win32.c @@ -1426,6 +1426,56 @@ demon_os_detach_process(DEMON_Entity *process){ return(result); } +internal DEMON_Handle +demon_os_create_snapshot(DEMON_Entity *thread) +{ + DEMON_Entity *process = thread->parent; + DEMON_W32_Ext *ext = demon_w32_ext(process); + OS_Handle result = os_handle_zero(); + HANDLE proc_all = OpenProcess(PROCESS_ALL_ACCESS, 0, GetProcessId(ext->proc.handle)); + if (!proc_all) { + return 0; + } + + HPSS snapshot = 0; + DWORD success = PssCaptureSnapshot( + proc_all, + PSS_CAPTURE_VA_CLONE, + 0, + &snapshot + ); + + CloseHandle(proc_all); + if (success != ERROR_SUCCESS) + { + return 0; + } + + PSS_VA_CLONE_INFORMATION info = {0}; + success = PssQuerySnapshot(snapshot, PSS_QUERY_VA_CLONE_INFORMATION, &info, sizeof(info)); + if (success != ERROR_SUCCESS) + { + PssFreeSnapshot(GetCurrentProcess(), snapshot); + return 0; + } + + DEMON_W32_Ext *snapshot_ext = demon_w32_ext_alloc(); + snapshot_ext->snapshot.handle = info.VaCloneHandle; + snapshot_ext->snapshot.snapshot_handle = snapshot; + + DEMON_Entity *entity = demon_ent_new(thread, DEMON_EntityKind_Snapshot, GetProcessId(info.VaCloneHandle)); + entity->ext = snapshot_ext; + return demon_ent_handle_from_ptr(entity); +} + +internal void +demon_os_snapshot_release(DEMON_Entity *entity) +{ + DEMON_W32_Ext *ext = demon_w32_ext(entity); + PssFreeSnapshot(GetCurrentProcess(), ext->snapshot.snapshot_handle); + demon_ent_release_root_and_children(entity); +} + //////////////////////////////// //~ rjf: @demon_os_hooks Entity Functions diff --git a/src/demon/win32/demon_os_win32.h b/src/demon/win32/demon_os_win32.h index 3fcea46a6..c60f282f7 100644 --- a/src/demon/win32/demon_os_win32.h +++ b/src/demon/win32/demon_os_win32.h @@ -44,6 +44,10 @@ union DEMON_W32_Ext B32 is_main; B32 name_is_unicode; } module; + struct { + HANDLE handle; + HPSS snapshot_handle; + } snapshot; }; //- helpers diff --git a/src/df/core/df_core.c b/src/df/core/df_core.c index cacf77189..097658e77 100644 --- a/src/df/core/df_core.c +++ b/src/df/core/df_core.c @@ -2771,6 +2771,44 @@ df_trap_net_from_thread__step_over_inst(Arena *arena, DF_Entity *thread) return result; } +internal B32 +df_advance_current_snapshot(Arena *arena, DF_CmdList *cmds, DF_Entity *thread) +{ + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if(child->kind == DF_EntityKind_Snapshot && child->b32 == 1) + { + child->b32 = 0; + if (!df_entity_is_nil(child->next)) + { + child->next->b32 = 1; + } + + DF_CmdParams params = df_cmd_params_zero(); + params.entity = df_handle_from_entity(thread); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_Entity); + df_cmd_list_push(arena, cmds, ¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_SelectThread)); + return 1; + } + } + + return 0; +} + +internal void +df_thread_snapshot(DF_Entity *thread) +{ + CTRL_Handle snapshot_handle = ctrl_thread_snapshot(thread->ctrl_machine_id, thread->ctrl_handle); + if (snapshot_handle.u64 == 0) { + return; + } + + DF_Entity *snapshot = df_entity_alloc(0, thread, DF_EntityKind_Snapshot); + df_entity_equip_ctrl_machine_id(snapshot, thread->ctrl_machine_id); + df_entity_equip_ctrl_handle(snapshot, snapshot_handle); + df_entity_equip_b32(snapshot, 0); +} + internal CTRL_TrapList df_trap_net_from_thread__step_over_line(Arena *arena, DF_Entity *thread) { @@ -2879,6 +2917,7 @@ df_trap_net_from_thread__step_over_line(Arena *arena, DF_Entity *thread) ctrl_trap_list_push(arena, &result, &trap); } + df_thread_snapshot(thread); scratch_end(scratch); return result; } @@ -2984,11 +3023,35 @@ df_trap_net_from_thread__step_into_line(Arena *arena, DF_Entity *thread) CTRL_Trap trap = {CTRL_TrapFlag_EndStepping, line_vaddr_rng.max}; ctrl_trap_list_push(arena, &result, &trap); } - + + df_thread_snapshot(thread); scratch_end(scratch); return result; } +internal void +df_trap_net_from_thread__step_back(Arena* arena, DF_Entity* thread) +{ + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if(child->kind == DF_EntityKind_Snapshot) + { + if (child->b32 == 1) { + DF_Entity *prev = child->prev; + if (!df_entity_is_nil(prev)) + { + child->b32 = 0; + prev->b32 = 1; + } + break; + } else if (child == thread->last) { + child->b32 = 1; + break; + } + } + } +} + //////////////////////////////// //~ rjf: Modules & Debug Info Mappings @@ -3449,6 +3512,22 @@ df_module_from_thread(DF_Entity *thread) return df_module_from_process_vaddr(process, rip); } +internal DF_Entity * +df_current_snapshot_from_thread(DF_Entity *thread) +{ + DF_Entity *snapshot = thread->parent; + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if(child->kind == DF_EntityKind_Snapshot && child->b32 == 1) + { + snapshot = child; + break; + } + } + + return snapshot; +} + internal U64 df_tls_base_vaddr_from_thread(DF_Entity *thread) { @@ -3950,7 +4029,7 @@ internal B32 df_eval_memory_read(void *u, void *out, U64 addr, U64 size) { DF_Entity *process = (DF_Entity *)u; - Assert(process->kind == DF_EntityKind_Process); + Assert(process->kind == DF_EntityKind_Process || process->kind == DF_EntityKind_Snapshot); Temp scratch = scratch_begin(0, 0); B32 result = 0; String8 data = ctrl_query_cached_data_from_process_vaddr_range(scratch.arena, process->ctrl_machine_id, process->ctrl_handle, r1u64(addr, addr+size)); @@ -4143,10 +4222,12 @@ df_eval_from_string(Arena *arena, DBGI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ //- rjf: unpack module info & produce eval machine DF_Entity *module = df_module_from_process_vaddr(process, thread_unwind_ip_vaddr); + DF_Entity *current_entity = df_current_snapshot_from_thread(thread); + U64 module_base = df_base_vaddr_from_module(module); U64 tls_base = df_tls_base_vaddr_from_thread(thread); EVAL_Machine machine = {0}; - machine.u = (void *)thread->parent; + machine.u = (void *)current_entity; machine.arch = arch; machine.memory_read = df_eval_memory_read; machine.reg_data = thread_unwind_regs_block; @@ -4232,7 +4313,7 @@ df_value_mode_eval_from_eval(TG_Graph *graph, RADDBG_Parsed *rdbg, DF_CtrlCtx *c { ProfBeginFunction(); DF_Entity *thread = df_entity_from_handle(ctrl_ctx->thread); - DF_Entity *process = thread->parent; + DF_Entity *process = df_current_snapshot_from_thread(thread); switch(eval.mode) { //- rjf: no work to be done. already in value mode @@ -6062,6 +6143,32 @@ df_push_active_target_list(Arena *arena) } //- rjf: per-run caches +internal void +df_clear_unwind_cache(DF_RunUnwindCache *cache) +{ + arena_clear(cache->arena); + cache->table_size = 1024; + cache->table = push_array(cache->arena, DF_RunUnwindCacheSlot, cache->table_size); +} + +internal U64 +df_hash_current_thread_snapshot(DF_Entity *thread, U32 *active_snapshot_index) +{ + DF_Handle handle = df_handle_from_entity(thread); + U64 hash = df_hash_from_string(str8_struct(&handle)); + *active_snapshot_index = 0; + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if (child->kind == DF_EntityKind_Snapshot && child->b32) { + break; + } + + (*active_snapshot_index)++; + } + + hash = df_hash_from_seed_string(hash, str8_struct(active_snapshot_index)); + return hash; +} internal DF_Unwind df_query_cached_unwind_from_thread(DF_Entity *thread) @@ -6072,12 +6179,13 @@ df_query_cached_unwind_from_thread(DF_Entity *thread) if(cache->table_size != 0) { DF_Handle handle = df_handle_from_entity(thread); - U64 hash = df_hash_from_string(str8_struct(&handle)); + U32 active_snapshot_index; + U64 hash = df_hash_current_thread_snapshot(thread, &active_snapshot_index); U64 slot_idx = hash % cache->table_size; DF_RunUnwindCacheSlot *slot = &cache->table[slot_idx]; for(DF_RunUnwindCacheNode *n = slot->first; n != 0; n = n->hash_next) { - if(df_handle_match(n->thread, handle)) + if(df_handle_match(n->thread, handle) && n->snapshot_index == active_snapshot_index) { result = n->unwind; break; @@ -6368,6 +6476,7 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) U64 new_reggen_idx = ctrl_reggen_idx(); //- rjf: consume & process events + B32 unwind_cache_clear = 1; CTRL_EventList events = ctrl_c2u_pop_events(scratch.arena); for(CTRL_EventNode *event_n = events.first; event_n != 0; event_n = event_n->next) { @@ -6387,6 +6496,7 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) { df_state->ctrl_is_running = 0; df_state->ctrl_soft_halt_issued = 0; + unwind_cache_clear = 0; DF_Entity *stop_thread = df_entity_from_ctrl_handle(event->machine_id, event->entity); // rjf: gather stop info @@ -6679,18 +6789,23 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) B32 good = 1; DF_EntityList all_threads = df_query_cached_entity_list_with_kind(DF_EntityKind_Thread); DF_RunUnwindCache *cache = &df_state->unwind_cache; - arena_clear(cache->arena); - cache->table_size = 1024; - cache->table = push_array(cache->arena, DF_RunUnwindCacheSlot, cache->table_size); + if (unwind_cache_clear || cache->table_size == 0) + { + df_clear_unwind_cache(cache); + } + for(DF_EntityNode *n = all_threads.first; n != 0; n = n->next) { DF_Entity *thread = n->entity; DF_Handle thread_handle = df_handle_from_entity(thread); - U64 hash = df_hash_from_string(str8_struct(&thread_handle)); + U32 active_snapshot_index; + U64 hash = df_hash_current_thread_snapshot(thread, &active_snapshot_index); + U64 slot_idx = hash % cache->table_size; DF_RunUnwindCacheSlot *slot = &cache->table[slot_idx]; DF_RunUnwindCacheNode *cache_node = push_array(cache->arena, DF_RunUnwindCacheNode, 1); cache_node->thread = thread_handle; + cache_node->snapshot_index = active_snapshot_index; cache_node->unwind = df_push_unwind_from_thread(cache->arena, thread); SLLQueuePush_NZ(0, slot->first, slot->last, cache_node, hash_next); if(cache_node->unwind.error != 0) @@ -7019,6 +7134,25 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) break; } } + + DF_EntityList threads = df_query_cached_entity_list_with_kind(DF_EntityKind_Thread); + for(DF_EntityNode *n = threads.first; n != 0; n = n->next) + { + DF_Entity *thread = n->entity; + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if (child->kind == DF_EntityKind_Snapshot) + { + child->b32 = 0; + ctrl_snapshot_release(child->ctrl_machine_id, child->ctrl_handle); + df_entity_mark_for_deletion(child); + } + } + } + + DF_RunUnwindCache *cache = &df_state->unwind_cache; + df_clear_unwind_cache(cache); + if(good_to_run) { df_ctrl_run(DF_RunKind_Run, &df_g_nil_entity, 0); @@ -7065,8 +7199,26 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) default: break; case DF_CoreCmdKind_StepIntoInst: {}break; case DF_CoreCmdKind_StepOverInst: {traps = df_trap_net_from_thread__step_over_inst(scratch.arena, thread);}break; - case DF_CoreCmdKind_StepIntoLine: {traps = df_trap_net_from_thread__step_into_line(scratch.arena, thread);}break; - case DF_CoreCmdKind_StepOverLine: {traps = df_trap_net_from_thread__step_over_line(scratch.arena, thread);}break; + case DF_CoreCmdKind_StepIntoLine: { + if (df_advance_current_snapshot(arena, cmds, thread)) + { + good = 0; + } + else + { + traps = df_trap_net_from_thread__step_into_line(scratch.arena, thread); + } + }break; + case DF_CoreCmdKind_StepOverLine: { + if (df_advance_current_snapshot(arena, cmds, thread)) + { + good = 0; + } + else + { + traps = df_trap_net_from_thread__step_over_line(scratch.arena, thread); + } + }break; case DF_CoreCmdKind_StepOut: { // rjf: thread => full unwind @@ -7200,7 +7352,20 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) df_cmd_list_push(arena, cmds, &p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_LaunchAndInit)); } }break; - + case DF_CoreCmdKind_StepBack: + { + DF_EntityList processes = df_query_cached_entity_list_with_kind(DF_EntityKind_Process); + if (processes.count != 0) + { + DF_Entity *thread = df_entity_from_handle(df_state->ctrl_ctx.thread); + df_trap_net_from_thread__step_back(scratch.arena, thread); + + DF_CmdParams params = df_cmd_params_zero(); + params.entity = df_handle_from_entity(thread); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_Entity); + df_cmd_list_push(arena, cmds, ¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_SelectThread)); + } + }break; //- rjf: solo-stepping mode case DF_CoreCmdKind_EnableSoloSteppingMode: { diff --git a/src/df/core/df_core.h b/src/df/core/df_core.h index 7fb976776..87f2c0ff8 100644 --- a/src/df/core/df_core.h +++ b/src/df/core/df_core.h @@ -976,6 +976,7 @@ struct DF_RunUnwindCacheNode DF_RunUnwindCacheNode *hash_next; DF_Handle thread; DF_Unwind unwind; + U32 snapshot_index; }; typedef struct DF_RunUnwindCacheSlot DF_RunUnwindCacheSlot; @@ -1497,6 +1498,7 @@ internal String8 df_debug_info_path_from_module(Arena *arena, DF_Entity *module) //////////////////////////////// //~ rjf: Stepping "Trap Net" Builders +internal B32 df_advance_current_snapshot(Arena *arena, DF_CmdList *cmds, DF_Entity *thread); internal CTRL_TrapList df_trap_net_from_thread__step_over_inst(Arena *arena, DF_Entity *thread); internal CTRL_TrapList df_trap_net_from_thread__step_over_line(Arena *arena, DF_Entity *thread); internal CTRL_TrapList df_trap_net_from_thread__step_into_line(Arena *arena, DF_Entity *thread); @@ -1542,6 +1544,7 @@ internal U64 df_type_num_from_binary_name(DF_Entity *binary, String8 name); //- rjf: thread info extraction helpers internal DF_Entity *df_module_from_process_vaddr(DF_Entity *process, U64 vaddr); internal DF_Entity *df_module_from_thread(DF_Entity *thread); +internal DF_Entity *df_current_snapshot_from_thread(DF_Entity *thread); internal U64 df_tls_base_vaddr_from_thread(DF_Entity *thread); internal Architecture df_architecture_from_entity(DF_Entity *entity); internal DF_Unwind df_push_unwind_from_thread(Arena *arena, DF_Entity *thread); @@ -1665,11 +1668,13 @@ internal DF_EntityList df_push_active_binary_list(Arena *arena); internal DF_EntityList df_push_active_target_list(Arena *arena); //- rjf: per-run caches +internal U64 df_hash_current_thread_snapshot(DF_Entity *thread, U32 *active_snapshot_index); internal DF_Unwind df_query_cached_unwind_from_thread(DF_Entity *thread); internal U64 df_query_cached_rip_from_thread(DF_Entity *thread); internal U64 df_query_cached_rip_from_thread_unwind(DF_Entity *thread, U64 unwind_count); internal EVAL_String2NumMap *df_query_cached_locals_map_from_binary_voff(DF_Entity *binary, U64 voff); internal EVAL_String2NumMap *df_query_cached_member_map_from_binary_voff(DF_Entity *binary, U64 voff); +internal void df_clear_unwind_cache(DF_RunUnwindCache *cache); //- rjf: top-level command dispatch internal void df_push_cmd__root(DF_CmdParams *params, DF_CmdSpec *spec); diff --git a/src/df/core/df_core.mdesk b/src/df/core/df_core.mdesk index 6806df48d..d2507e236 100644 --- a/src/df/core/df_core.mdesk +++ b/src/df/core/df_core.mdesk @@ -62,6 +62,7 @@ DF_EntityKindTable: //- rjf: control system entities {CtrlRequest ctrl_request 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "Control Request" } {Process process 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Threads "Process" } + {Snapshot snapshot 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "Snapshot" } {Thread thread 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Thread "Thread" } {Module module 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Module "Module" } {DebugInfoOverride debug_info_override 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 "Label" Null "Debug Info Override" } @@ -69,7 +70,7 @@ DF_EntityKindTable: //- rjf: parser task entities {ConversionTask conversion_task 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "Conversion Task" } {ConversionFail conversion_fail 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "Conversion Failure" } - + //- rjf: history {EndedProcess ended_process 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "EndedProcess" } } @@ -128,6 +129,7 @@ DF_CoreCmdTable:// | | | {StepIntoLine 0 Null Nil 0 0 0 0 0 0 StepInto "step_into_line" "Step Into (Line)" "Performs a step that goes into calls, at the source code line level." "step,thread" } {StepOverLine 0 Null Nil 0 0 0 0 0 0 StepOver "step_over_line" "Step Over (Line)" "Performs a step that skips calls, at the source code line level." "step,thread" } {StepOut 0 Null Nil 0 0 0 0 0 0 StepOut "step_out" "Step Out" "Runs to the end of the current function and exits it." "" } + {StepBack 0 Null Nil 0 0 0 0 0 0 LeftArrow "step_back" "Step Back" "Steps back in the execution." "step,back" } {RunToAddress 0 VirtualAddr Nil 0 0 0 0 1 1 PlayStepForward "run_to_address" "Run To Address" "Runs the selected thread to the specified address." "" } {Halt 0 Null Nil 0 0 0 0 0 0 Pause "halt" "Halt" "Halts all running processes." "pause" } {SoftHaltRefresh 0 Null Nil 0 0 0 0 0 0 Refresh "soft_halt_refresh" "Soft Halt Refresh" "Interrupts all running processes to collect data, and then resumes them." "" } diff --git a/src/df/core/generated/df_core.meta.c b/src/df/core/generated/df_core.meta.c index 2f91d78ad..4d57f2ff7 100644 --- a/src/df/core/generated/df_core.meta.c +++ b/src/df/core/generated/df_core.meta.c @@ -20,6 +20,7 @@ DF_CmdSpecInfo df_g_core_cmd_kind_spec_info_table[] = { str8_lit_comp("step_into_line"), str8_lit_comp("Performs a step that goes into calls, at the source code line level."), str8_lit_comp("step,thread"), str8_lit_comp("Step Into (Line)"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_StepInto}, { str8_lit_comp("step_over_line"), str8_lit_comp("Performs a step that skips calls, at the source code line level."), str8_lit_comp("step,thread"), str8_lit_comp("Step Over (Line)"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_StepOver}, { str8_lit_comp("step_out"), str8_lit_comp("Runs to the end of the current function and exits it."), str8_lit_comp(""), str8_lit_comp("Step Out"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_StepOut}, +{ str8_lit_comp("step_back"), str8_lit_comp("Steps back in the execution."), str8_lit_comp("step,back"), str8_lit_comp("Step Back"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_LeftArrow}, { str8_lit_comp("run_to_address"), str8_lit_comp("Runs the selected thread to the specified address."), str8_lit_comp(""), str8_lit_comp("Run To Address"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_VirtualAddr, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*1)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*1)}, DF_IconKind_PlayStepForward}, { str8_lit_comp("halt"), str8_lit_comp("Halts all running processes."), str8_lit_comp("pause"), str8_lit_comp("Halt"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_Pause}, { str8_lit_comp("soft_halt_refresh"), str8_lit_comp("Interrupts all running processes to collect data, and then resumes them."), str8_lit_comp(""), str8_lit_comp("Soft Halt Refresh"), (DF_CmdSpecFlag_OmitFromLists*0), {DF_CmdParamSlot_Null, DF_EntityKind_Nil, (DF_CmdQueryFlag_AllowFiles*0)|(DF_CmdQueryFlag_AllowFolders*0)|(DF_CmdQueryFlag_CodeInput*0)|(DF_CmdQueryFlag_KeepOldInput*0)|(DF_CmdQueryFlag_SelectOldInput*0)|(DF_CmdQueryFlag_Required*0)}, DF_IconKind_Refresh}, diff --git a/src/df/core/generated/df_core.meta.h b/src/df/core/generated/df_core.meta.h index df6a5457a..c8b108d88 100644 --- a/src/df/core/generated/df_core.meta.h +++ b/src/df/core/generated/df_core.meta.h @@ -36,6 +36,7 @@ DF_EntityKind_Source, DF_EntityKind_Dest, DF_EntityKind_CtrlRequest, DF_EntityKind_Process, +DF_EntityKind_Snapshot, DF_EntityKind_Thread, DF_EntityKind_Module, DF_EntityKind_DebugInfoOverride, @@ -70,6 +71,7 @@ DF_CoreCmdKind_StepOverInst, DF_CoreCmdKind_StepIntoLine, DF_CoreCmdKind_StepOverLine, DF_CoreCmdKind_StepOut, +DF_CoreCmdKind_StepBack, DF_CoreCmdKind_RunToAddress, DF_CoreCmdKind_Halt, DF_CoreCmdKind_SoftHaltRefresh, @@ -1546,6 +1548,7 @@ DF_IconKind_Null, DF_IconKind_Null, DF_IconKind_Null, DF_IconKind_Threads, +DF_IconKind_Null, DF_IconKind_Thread, DF_IconKind_Module, DF_IconKind_Null, @@ -1576,6 +1579,7 @@ str8_lit_comp("Source"), str8_lit_comp("Destination"), str8_lit_comp("Control Request"), str8_lit_comp("Process"), +str8_lit_comp("Snapshot"), str8_lit_comp("Thread"), str8_lit_comp("Module"), str8_lit_comp("Debug Info Override"), @@ -1612,6 +1616,7 @@ str8_lit_comp("Label"), str8_lit_comp("Label"), str8_lit_comp("Label"), str8_lit_comp("Label"), +str8_lit_comp("Label"), }; DF_EntityKindFlags df_g_entity_kind_flags_table[] = @@ -1638,6 +1643,7 @@ DF_EntityKindFlags df_g_entity_kind_flags_table[] = (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), +(0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 1*DF_EntityKindFlag_LeafMutationSoftHalt | 1*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 1*DF_EntityKindFlag_TreeMutationSoftHalt | 1*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProfileConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProfileConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), @@ -1667,6 +1673,7 @@ DF_EntityOpFlags df_g_entity_kind_op_flags_table[] = (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (1*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (1*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), +(0*DF_EntityOpFlag_Delete) | (1*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index c44fe3102..0153d9c51 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -2072,7 +2072,8 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D if(thread->kind == DF_EntityKind_Thread) { // rjf: grab rip - U64 rip_vaddr = (unwind_count == 0 ? df_rip_from_thread(thread) : df_query_cached_rip_from_thread_unwind(thread, unwind_count)); + B32 use_current_rip = unwind_count == 0 && df_entity_is_nil(thread->first); + U64 rip_vaddr = (use_current_rip ? df_rip_from_thread(thread) : df_query_cached_rip_from_thread_unwind(thread, unwind_count)); // rjf: extract thread/rip info DF_Entity *process = df_entity_ancestor_from_kind(thread, DF_EntityKind_Process); @@ -4306,6 +4307,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D DF_CoreCmdKind_StepInto, DF_CoreCmdKind_StepOver, DF_CoreCmdKind_StepOut, + DF_CoreCmdKind_StepBack, DF_CoreCmdKind_Attach, }; U32 codepoints[] = @@ -4318,6 +4320,7 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D 'i', 'o', 't', + 'b', 'a', }; Assert(ArrayCount(codepoints) == ArrayCount(cmds)); @@ -9132,12 +9135,24 @@ df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_ ui_set_next_pref_height(ui_pct(1, 0)); ui_set_next_text_color(color); ui_set_next_text_alignment(UI_TextAlign_Center); + + B32 is_snapshot_active = 0; + for(DF_Entity *child = thread->first; !df_entity_is_nil(child); child = child->next) + { + if (child->kind == DF_EntityKind_Snapshot && child->b32 == 1) + { + is_snapshot_active = 1; + break; + } + } + + DF_IconKind icon_kind = is_snapshot_active ? DF_IconKind_Undo : DF_IconKind_RightArrow; UI_Box *thread_box = ui_build_box_from_stringf(UI_BoxFlag_DisableTextTrunc| UI_BoxFlag_Clickable| UI_BoxFlag_AnimatePosX| UI_BoxFlag_DrawText, "%S##ip_%p", - df_g_icon_kind_text_table[DF_IconKind_RightArrow], + df_g_icon_kind_text_table[icon_kind], thread); UI_Signal thread_sig = ui_signal_from_box(thread_box); diff --git a/src/df/gfx/df_gfx.mdesk b/src/df/gfx/df_gfx.mdesk index b9b9537cd..89f32eed7 100644 --- a/src/df/gfx/df_gfx.mdesk +++ b/src/df/gfx/df_gfx.mdesk @@ -20,6 +20,7 @@ DF_DefaultBindingTable: { "step_into_inst" F11 0 0 alt } { "step_over_inst" F10 0 0 alt } { "step_out" F11 0 shift 0 } + { "step_back" F8 0 0 0 } { "halt" X ctrl shift 0 } { "halt" Pause 0 0 0 } { "soft_halt_refresh" R 0 0 alt } @@ -672,6 +673,7 @@ raddbg_readme: @p "**F10**: Step Over"; @p "**F11**: Step Into"; @p "**Shift + F11**: Step Out"; + @p "**F8**: Step Back"; @p "**F5**: Run"; @p "**Ctrl + Shift + X**, or **Pause**: Halt All Processes"; @p "**Shift + F5**: Kill All Processes"; diff --git a/src/df/gfx/generated/df_gfx.meta.h b/src/df/gfx/generated/df_gfx.meta.h index c98a637a7..31f0c295b 100644 --- a/src/df/gfx/generated/df_gfx.meta.h +++ b/src/df/gfx/generated/df_gfx.meta.h @@ -903,6 +903,7 @@ DF_StringBindingPair df_g_default_binding_table[] = {str8_lit_comp("step_into_inst"), {OS_Key_F11, 0 |OS_EventFlag_Alt}}, {str8_lit_comp("step_over_inst"), {OS_Key_F10, 0 |OS_EventFlag_Alt}}, {str8_lit_comp("step_out"), {OS_Key_F11, 0 |OS_EventFlag_Shift }}, +{str8_lit_comp("step_back"), {OS_Key_F8, 0 }}, {str8_lit_comp("halt"), {OS_Key_X, 0 |OS_EventFlag_Ctrl |OS_EventFlag_Shift }}, {str8_lit_comp("halt"), {OS_Key_Pause, 0 }}, {str8_lit_comp("soft_halt_refresh"), {OS_Key_R, 0 |OS_EventFlag_Alt}}, diff --git a/src/os/core/win32/os_core_win32.h b/src/os/core/win32/os_core_win32.h index 3ce6aac47..90399d396 100644 --- a/src/os/core/win32/os_core_win32.h +++ b/src/os/core/win32/os_core_win32.h @@ -13,6 +13,7 @@ #include #include +#include #if OS_FEATURE_GRAPHICAL #include