mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-13 07:32:23 -07:00
demon/win32: roll back on all hit traps, even if explicit. the previous implementation would silently skip threads past explicit traps that they hit, as a way of implicitly storing the fact that trap exceptions had been reported, and the user could continue past them. this resulted in incorrect instruction pointer display in those circumstances. this change adjusts this, so that after a trap exception of any kind, the instruction pointer is ALWAYS rolled back. to ensure that the trap is not repeatedly hit, if the associated exception has already been reported, to allow the user to e.g. step over traps (this is the behavior of Visual Studio), additional state is stored per-thread-entity, which allows a subsequent demon_os_run to adjust RIPs past their previously reported traps before running again.
This commit is contained in:
@@ -767,7 +767,9 @@ ctrl_process_write(CTRL_MachineID machine_id, CTRL_Handle process, Rng1U64 range
|
||||
ins_atomic_u64_inc_eval(&ctrl_state->memgen_idx);
|
||||
}
|
||||
|
||||
//- rjf: success -> forcibly, synchronously update cache, for small regions
|
||||
//- rjf: success -> wait for cache updates, for small regions - prefer relatively seamless
|
||||
// writes within calling frame's "view" of the memory, at the expense of a small amount of
|
||||
// time.
|
||||
if(result)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
@@ -144,11 +144,7 @@ demon_accel_read_regs(DEMON_Entity *thread){
|
||||
// update reg cache
|
||||
if (accel->reg_cache_time != demon_time){
|
||||
accel->reg_cache_time = demon_time;
|
||||
B32 success = 0;
|
||||
switch (thread->arch){
|
||||
case Architecture_x86:{success = demon_os_read_regs_x86(thread, &accel->regs.x86);}break;
|
||||
case Architecture_x64:{success = demon_os_read_regs_x64(thread, &accel->regs.x64);}break;
|
||||
}
|
||||
B32 success = demon_os_read_regs(thread, &accel->regs);
|
||||
if (!success){
|
||||
MemoryZeroStruct(&accel->regs);
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ struct DEMON_Entity
|
||||
DEMON_Entity *first;
|
||||
DEMON_Entity *last;
|
||||
|
||||
U16 kind;
|
||||
U16 arch;
|
||||
DEMON_EntityKind kind;
|
||||
Architecture arch;
|
||||
U32 gen;
|
||||
U64 id;
|
||||
U64 addr_range_dim;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "demon_core.c"
|
||||
#include "demon_common.c"
|
||||
#include "demon_accel.c"
|
||||
#include "demon_os.c"
|
||||
|
||||
#if OS_WINDOWS
|
||||
# include "win32/demon_os_win32.c"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
internal B32
|
||||
demon_os_read_regs(DEMON_Entity *thread, void *dst)
|
||||
{
|
||||
B32 result = 0;
|
||||
switch(thread->arch)
|
||||
{
|
||||
default:{}break;
|
||||
case Architecture_x86:{result = demon_os_read_regs_x86(thread, (REGS_RegBlockX86 *)dst);}break;
|
||||
case Architecture_x64:{result = demon_os_read_regs_x64(thread, (REGS_RegBlockX64 *)dst);}break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal B32
|
||||
demon_os_write_regs(DEMON_Entity *thread, void *src)
|
||||
{
|
||||
B32 result = 0;
|
||||
switch(thread->arch)
|
||||
{
|
||||
default:{}break;
|
||||
case Architecture_x86:{result = demon_os_write_regs_x86(thread, (REGS_RegBlockX86 *)src);}break;
|
||||
case Architecture_x64:{result = demon_os_write_regs_x64(thread, (REGS_RegBlockX64 *)src);}break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -35,6 +35,12 @@ struct DEMON_OS_RunCtrls
|
||||
U64 trap_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Helpers
|
||||
|
||||
internal B32 demon_os_read_regs(DEMON_Entity *thread, void *dst);
|
||||
internal B32 demon_os_write_regs(DEMON_Entity *thread, void *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: @demon_os_hooks Main Layer Initialization
|
||||
|
||||
|
||||
@@ -571,20 +571,24 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
}
|
||||
}
|
||||
|
||||
// prep threads that will be allowed to run
|
||||
for (DEMON_W32_EntityNode *node = first_run_thread;
|
||||
node != 0;
|
||||
node = node->next){
|
||||
// prep suspension state of threads that will be allowed to run
|
||||
for(DEMON_W32_EntityNode *node = first_run_thread;
|
||||
node != 0;
|
||||
node = node->next)
|
||||
{
|
||||
DEMON_Entity *thread = node->entity;
|
||||
DEMON_W32_Ext *thread_ext = demon_w32_ext(thread);
|
||||
DWORD resume_result = ResumeThread(thread_ext->thread.handle);
|
||||
if (resume_result == max_U32){
|
||||
if(resume_result == max_U32)
|
||||
{
|
||||
// TODO(allen): Error. Unknown cause (do GetLastError, FromatMessage)
|
||||
}
|
||||
else{
|
||||
else
|
||||
{
|
||||
DWORD desired_counter = 0;
|
||||
DWORD current_counter = resume_result - 1;
|
||||
if (current_counter != desired_counter){
|
||||
if(current_counter != desired_counter)
|
||||
{
|
||||
// NOTE(rjf): Warning. The user has manually suspended this thread,
|
||||
// so even though from Demon's perspective it thinks this thread
|
||||
// should run, it will not, because the user has manually called
|
||||
@@ -593,6 +597,33 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: if run threads are marked as having reported an explicit trap
|
||||
// on their last run, shift their RIPs past that trap instruction, so
|
||||
// that they may continue
|
||||
for(DEMON_W32_EntityNode *node = first_run_thread;
|
||||
node != 0;
|
||||
node = node->next)
|
||||
{
|
||||
DEMON_Entity *thread = node->entity;
|
||||
DEMON_W32_Ext *thread_ext = demon_w32_ext(thread);
|
||||
if(thread_ext->thread.last_run_reported_trap)
|
||||
{
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
U64 regs_block_size = regs_block_size_from_architecture(thread->arch);
|
||||
void *regs_block = push_array(temp.arena, U8, regs_block_size);
|
||||
B32 good = demon_os_read_regs(thread, regs_block);
|
||||
U64 pre_rip = regs_rip_from_arch_block(thread->arch, regs_block);
|
||||
if(good && pre_rip == thread_ext->thread.last_run_reported_trap_pre_rip)
|
||||
{
|
||||
regs_arch_block_write_rip(thread->arch, regs_block, thread_ext->thread.last_run_reported_trap_post_rip);
|
||||
demon_os_write_regs(thread, regs_block);
|
||||
}
|
||||
temp_end(temp);
|
||||
thread_ext->thread.last_run_reported_trap = 0;
|
||||
thread_ext->thread.last_run_reported_trap_post_rip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// send last saved continue signal
|
||||
B32 good_state = 1;
|
||||
if (demon_w32_resume_needed != 0){
|
||||
@@ -988,27 +1019,22 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
}
|
||||
|
||||
// rjf: determine whether to roll back instruction pointer
|
||||
B32 rollback = (is_trap && !hit_explicit_trap);
|
||||
B32 rollback = (is_trap);
|
||||
|
||||
// rjf: roll back
|
||||
if (rollback){
|
||||
// TODO(allen): possibly buggy
|
||||
switch (thread->arch){
|
||||
case Architecture_x86:
|
||||
{
|
||||
REGS_RegBlockX86 regs = {0};
|
||||
demon_os_read_regs_x86(thread, ®s);
|
||||
regs.eip.u32 = instruction_pointer;
|
||||
demon_os_write_regs_x86(thread, ®s);
|
||||
}break;
|
||||
case Architecture_x64:
|
||||
{
|
||||
REGS_RegBlockX64 regs = {0};
|
||||
demon_os_read_regs_x64(thread, ®s);
|
||||
regs.rip.u64 = instruction_pointer;
|
||||
demon_os_write_regs_x64(thread, ®s);
|
||||
}break;
|
||||
U64 post_trap_rip = 0;
|
||||
if(rollback)
|
||||
{
|
||||
Temp temp = temp_begin(scratch.arena);
|
||||
U64 regs_block_size = regs_block_size_from_architecture(thread->arch);
|
||||
void *regs_block = push_array(scratch.arena, U8, regs_block_size);
|
||||
if(demon_os_read_regs(thread, regs_block))
|
||||
{
|
||||
post_trap_rip = regs_rip_from_arch_block(thread->arch, regs_block);
|
||||
regs_arch_block_write_rip(thread->arch, regs_block, instruction_pointer);
|
||||
demon_os_write_regs(thread, regs_block);
|
||||
}
|
||||
temp_end(temp);
|
||||
}
|
||||
|
||||
// allen: if this is not a user trap or explicit trap it's a previous trap
|
||||
@@ -1018,7 +1044,9 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
B32 skip_event = (hit_previous_trap);
|
||||
|
||||
// emit event
|
||||
if (!skip_event){
|
||||
if(!skip_event)
|
||||
{
|
||||
// rjf: fill top-level info
|
||||
DEMON_Event *e = demon_push_event(arena, &result, DEMON_EventKind_Exception);
|
||||
e->process = demon_ent_handle_from_ptr(process);
|
||||
e->thread = demon_ent_handle_from_ptr(thread);
|
||||
@@ -1026,6 +1054,16 @@ demon_os_run(Arena *arena, DEMON_OS_RunCtrls *ctrls){
|
||||
e->flags = exception->ExceptionFlags;
|
||||
e->instruction_pointer = (U64)exception->ExceptionAddress;
|
||||
|
||||
// rjf: explicit trap -> mark this thread as having reported this trap
|
||||
if(hit_explicit_trap)
|
||||
{
|
||||
DEMON_W32_Ext *thread_ext = demon_w32_ext(thread);
|
||||
thread_ext->thread.last_run_reported_trap = 1;
|
||||
thread_ext->thread.last_run_reported_trap_pre_rip = instruction_pointer;
|
||||
thread_ext->thread.last_run_reported_trap_post_rip = post_trap_rip;
|
||||
}
|
||||
|
||||
// rjf: fill by exception code
|
||||
switch (exception->ExceptionCode){
|
||||
case DEMON_W32_EXCEPTION_BREAKPOINT:
|
||||
{
|
||||
|
||||
@@ -37,6 +37,9 @@ union DEMON_W32_Ext
|
||||
U64 thread_local_base;
|
||||
U64 last_name_hash;
|
||||
U64 name_gather_time_us;
|
||||
B32 last_run_reported_trap;
|
||||
U64 last_run_reported_trap_pre_rip;
|
||||
U64 last_run_reported_trap_post_rip;
|
||||
} thread;
|
||||
struct{
|
||||
HANDLE handle;
|
||||
|
||||
@@ -2266,6 +2266,31 @@ debug_string_tests(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Interrupt Stepping Tests
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static void
|
||||
interrupt_stepping_tests(void)
|
||||
{
|
||||
for(int i = 0; i < 1000; i += 1)
|
||||
{
|
||||
if(i == 999)
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 1000; i += 1)
|
||||
{
|
||||
if(i == 999)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
int x = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// NOTE(allen): Exception Stepping
|
||||
|
||||
@@ -2561,6 +2586,8 @@ mule_main(int argc, char** argv){
|
||||
|
||||
debug_string_tests();
|
||||
|
||||
interrupt_stepping_tests();
|
||||
|
||||
exception_stepping_tests();
|
||||
|
||||
return(0);
|
||||
|
||||
Reference in New Issue
Block a user