mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
WIP changed backend to use ptrace seize and interrupt
This commit is contained in:
committed by
Ryan Fleury
parent
80ccfba3c6
commit
ddc57a0ac4
+348
-279
@@ -4,6 +4,16 @@
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Helpers
|
//~ rjf: Helpers
|
||||||
|
|
||||||
|
internal DMN_LNX_EntityNode *
|
||||||
|
dmn_lnx_entity_list_push(Arena *arena, DMN_LNX_EntityList *list, DMN_LNX_Entity *v)
|
||||||
|
{
|
||||||
|
DMN_LNX_EntityNode *n = push_array(arena, DMN_LNX_EntityNode, 1);
|
||||||
|
n->v = v;
|
||||||
|
SLLQueuePush(list->first, list->last, n);
|
||||||
|
list->count += 1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
//- rjf: file descriptor memory reading/writing helpers
|
//- rjf: file descriptor memory reading/writing helpers
|
||||||
|
|
||||||
internal U64
|
internal U64
|
||||||
@@ -293,39 +303,28 @@ dmn_lnx_exe_path_from_pid(Arena *arena, pid_t pid)
|
|||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&arena, 1);
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
//- rjf: get exe link path
|
|
||||||
String8 exe_link_path = str8f(scratch.arena, "/proc/%d/exe", pid);
|
String8 exe_link_path = str8f(scratch.arena, "/proc/%d/exe", pid);
|
||||||
|
String8List parts = {0};
|
||||||
//- rjf: read the link
|
|
||||||
Temp restore_point = temp_begin(arena);
|
|
||||||
B32 good = 0;
|
|
||||||
U8 *buffer = 0;
|
|
||||||
int readlink_result = 0;
|
int readlink_result = 0;
|
||||||
S64 cap = PATH_MAX;
|
for(S64 r = 0, cap = PATH_MAX; r < 4; cap *= 2, r += 1)
|
||||||
for(S64 r = 0; r < 4; cap *= 2, r += 1)
|
|
||||||
{
|
{
|
||||||
temp_end(restore_point);
|
U8 *buffer = push_array(arena, U8, cap);
|
||||||
buffer = push_array_no_zero(arena, U8, cap);
|
|
||||||
readlink_result = readlink((char *)exe_link_path.str, (char *)buffer, cap);
|
readlink_result = readlink((char *)exe_link_path.str, (char *)buffer, cap);
|
||||||
|
|
||||||
|
if(readlink_result < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
str8_list_push(scratch.arena, &parts, str8(buffer, readlink_result));
|
||||||
|
|
||||||
if(readlink_result < cap)
|
if(readlink_result < cap)
|
||||||
{
|
{
|
||||||
good = 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- rjf: package result
|
String8 result = str8_list_join(arena, &parts, 0);
|
||||||
String8 result = {0};
|
|
||||||
if(!good || readlink_result == -1)
|
|
||||||
{
|
|
||||||
temp_end(restore_point);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arena_pop(arena, (cap - readlink_result - 1));
|
|
||||||
result = str8(buffer, readlink_result + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -860,7 +859,7 @@ dmn_lnx_thread_write_ip(DMN_LNX_Entity *thread, U64 ip)
|
|||||||
{
|
{
|
||||||
REGS_RegBlockX64 *reg_block = thread->reg_block;
|
REGS_RegBlockX64 *reg_block = thread->reg_block;
|
||||||
regs_arch_block_write_rip(thread->arch, reg_block, ip);
|
regs_arch_block_write_rip(thread->arch, reg_block, ip);
|
||||||
is_ip_written = 1;
|
is_ip_written = dmn_lnx_thread_write_reg_block(thread, reg_block);
|
||||||
}
|
}
|
||||||
Assert(is_ip_written);
|
Assert(is_ip_written);
|
||||||
return is_ip_written;
|
return is_ip_written;
|
||||||
@@ -874,7 +873,7 @@ dmn_lnx_thread_write_sp(DMN_LNX_Entity *thread, U64 sp)
|
|||||||
{
|
{
|
||||||
REGS_RegBlockX64 *reg_block = thread->reg_block;
|
REGS_RegBlockX64 *reg_block = thread->reg_block;
|
||||||
regs_arch_block_write_rsp(thread->arch, reg_block, sp);
|
regs_arch_block_write_rsp(thread->arch, reg_block, sp);
|
||||||
is_sp_written = 1;
|
is_sp_written = dmn_lnx_thread_write_reg_block(thread, reg_block);
|
||||||
}
|
}
|
||||||
Assert(is_sp_written);
|
Assert(is_sp_written);
|
||||||
return is_sp_written;
|
return is_sp_written;
|
||||||
@@ -1331,7 +1330,7 @@ dmn_lnx_set_single_step_flag(DMN_LNX_Entity *thread, B32 is_on)
|
|||||||
{
|
{
|
||||||
reg_block->rflags.u64 &= ~X64_RFlag_Trap;
|
reg_block->rflags.u64 &= ~X64_RFlag_Trap;
|
||||||
}
|
}
|
||||||
is_flag_set = 1;
|
is_flag_set = dmn_lnx_thread_write_reg_block(thread, thread->reg_block);
|
||||||
}break;
|
}break;
|
||||||
case Arch_x86:
|
case Arch_x86:
|
||||||
case Arch_arm32:
|
case Arch_arm32:
|
||||||
@@ -1340,6 +1339,7 @@ dmn_lnx_set_single_step_flag(DMN_LNX_Entity *thread, B32 is_on)
|
|||||||
NotImplemented;
|
NotImplemented;
|
||||||
}break;
|
}break;
|
||||||
}
|
}
|
||||||
|
Assert(is_flag_set);
|
||||||
return is_flag_set;
|
return is_flag_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1544,9 +1544,12 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
|
|||||||
pid_t pid = 0;
|
pid_t pid = 0;
|
||||||
int ptrace_result = 0;
|
int ptrace_result = 0;
|
||||||
int chdir_result = 0;
|
int chdir_result = 0;
|
||||||
int setoptions_result = 0;
|
|
||||||
B32 error__need_child_kill = 0;
|
B32 error__need_child_kill = 0;
|
||||||
|
|
||||||
|
// open temp pipes to communicate with child process
|
||||||
|
int pipe_fds[2] = {0};
|
||||||
|
if (pipe(&pipe_fds[0]) < 0) { InvalidPath; }
|
||||||
|
|
||||||
//- rjf: fork
|
//- rjf: fork
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if(pid == -1) { goto error; }
|
if(pid == -1) { goto error; }
|
||||||
@@ -1554,9 +1557,9 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
|
|||||||
//- rjf: child process -> execute actual target
|
//- rjf: child process -> execute actual target
|
||||||
if(pid == 0)
|
if(pid == 0)
|
||||||
{
|
{
|
||||||
// turn the thread into a tracee
|
// wait for parent seize
|
||||||
ptrace_result = ptrace(PTRACE_TRACEME, 0, 0, 0);
|
char b;
|
||||||
if(ptrace_result == -1) { goto error; }
|
read(pipe_fds[0], &b, sizeof(b));
|
||||||
|
|
||||||
// set current working directory to tracee
|
// set current working directory to tracee
|
||||||
chdir_result = chdir(path);
|
chdir_result = chdir(path);
|
||||||
@@ -1572,32 +1575,59 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
|
|||||||
//- rjf: parent process
|
//- rjf: parent process
|
||||||
if(pid != 0)
|
if(pid != 0)
|
||||||
{
|
{
|
||||||
//- rjf: wait for child
|
enum LaunchStatus
|
||||||
int status = 0;
|
|
||||||
pid_t wait_id = waitpid(pid, &status, __WALL);
|
|
||||||
if(wait_id != pid)
|
|
||||||
{
|
|
||||||
// NOTE(rjf): we do not know what this means - needs study if this actually arises.
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
//- rjf: determine child launch status
|
|
||||||
typedef enum LaunchStatus
|
|
||||||
{
|
{
|
||||||
LaunchStatus_Null,
|
LaunchStatus_Null,
|
||||||
LaunchStatus_FailBeforePtrace,
|
LaunchStatus_FailBeforePtrace,
|
||||||
LaunchStatus_FailAfterPtrace,
|
LaunchStatus_FailAfterPtrace,
|
||||||
LaunchStatus_Success,
|
LaunchStatus_Success,
|
||||||
}
|
};
|
||||||
LaunchStatus;
|
enum LaunchStatus launch_status = LaunchStatus_FailBeforePtrace;
|
||||||
LaunchStatus launch_status = LaunchStatus_Null;
|
|
||||||
{
|
{
|
||||||
B32 wifstopped = WIFSTOPPED(status);
|
int s = 0;
|
||||||
int wstopsig = WSTOPSIG(status);
|
|
||||||
if(0){}
|
// seize process
|
||||||
else if(wifstopped && wstopsig == SIGTRAP) { launch_status = LaunchStatus_Success; }
|
if (ptrace(PTRACE_SEIZE, pid, 0, 0) < 0) { Assert(0 && "seize failed"); goto launch_error; }
|
||||||
else if(wifstopped && wstopsig != SIGTRAP) { launch_status = LaunchStatus_FailAfterPtrace; }
|
|
||||||
else { launch_status = LaunchStatus_FailBeforePtrace; }
|
// interrupt process
|
||||||
|
launch_status = LaunchStatus_FailAfterPtrace;
|
||||||
|
if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) < 0) { Assert(0 && "interrupt failed"); goto launch_error; }
|
||||||
|
if (waitpid(pid, &s, __WALL|__WNOTHREAD) < 0) { Assert(0 && "interrupt wait failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// entry read
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// resume child
|
||||||
|
char b = 1;
|
||||||
|
if (write(pipe_fds[1], &b, sizeof(b)) < 0) { Assert(0 && "resume child failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// exit read
|
||||||
|
if (waitpid(pid, &s, __WALL|__WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// entry chdir
|
||||||
|
if (waitpid(pid, &s, __WALL|__WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// exit chdir
|
||||||
|
if (waitpid(pid, &s, __WALL|__WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// entry execv
|
||||||
|
if (waitpid(pid, &s, __WALL|__WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
|
||||||
|
// exit execve
|
||||||
|
if (waitpid(pid, &s, __WALL | __WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) { Assert(0 && "syscall failed"); goto launch_error; }
|
||||||
|
if (waitpid(pid, &s, __WALL | __WNOTHREAD) < 0) { Assert(0 && "wait failed"); goto launch_error; }
|
||||||
|
|
||||||
|
uintptr_t trace_options = PTRACE_O_TRACEEXIT | PTRACE_O_EXITKILL | PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEVFORKDONE;
|
||||||
|
int setoptions_result = ptrace(PTRACE_SETOPTIONS, pid, 0, PtrFromInt(trace_options));
|
||||||
|
if (setoptions_result == -1) { Assert(0 && "failed to set options"); error__need_child_kill = 1; goto launch_error; }
|
||||||
|
|
||||||
|
launch_status = LaunchStatus_Success;
|
||||||
|
launch_error:;
|
||||||
}
|
}
|
||||||
|
|
||||||
//- rjf: respond to launch status appropriately
|
//- rjf: respond to launch status appropriately
|
||||||
@@ -1636,10 +1666,6 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
|
|||||||
//- rjf: successful launch
|
//- rjf: successful launch
|
||||||
case LaunchStatus_Success:
|
case LaunchStatus_Success:
|
||||||
{
|
{
|
||||||
uintptr_t trace_options = PTRACE_O_TRACEEXIT|PTRACE_O_EXITKILL|PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK|PTRACE_O_TRACECLONE;
|
|
||||||
setoptions_result = ptrace(PTRACE_SETOPTIONS, pid, 0, PtrFromInt(trace_options));
|
|
||||||
if(setoptions_result == -1) { error__need_child_kill = 1; goto error; }
|
|
||||||
|
|
||||||
ELF_Hdr64 exe_ehdr = dmn_lnx_ehdr_from_pid(pid);
|
ELF_Hdr64 exe_ehdr = dmn_lnx_ehdr_from_pid(pid);
|
||||||
int memory_fd = open((char*)str8f(scratch.arena, "/proc/%d/mem", pid).str, O_RDWR);
|
int memory_fd = open((char*)str8f(scratch.arena, "/proc/%d/mem", pid).str, O_RDWR);
|
||||||
DMN_LNX_ProcessAuxv auxv = dmn_lnx_auxv_from_pid(pid, exe_ehdr.e_ident[ELF_Identifier_Class]);
|
DMN_LNX_ProcessAuxv auxv = dmn_lnx_auxv_from_pid(pid, exe_ehdr.e_ident[ELF_Identifier_Class]);
|
||||||
@@ -1890,6 +1916,10 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
|
|||||||
|
|
||||||
//- rjf: success
|
//- rjf: success
|
||||||
success:;
|
success:;
|
||||||
|
|
||||||
|
// clean up pipes
|
||||||
|
close(pipe_fds[0]);
|
||||||
|
close(pipe_fds[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
@@ -1928,211 +1958,14 @@ dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DMN_EventList
|
internal void
|
||||||
dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
dmn_lnx_wait_for_events(Arena *arena, DMN_EventList *evts, pid_t tid, B32 wait_for_group_stop, DMN_ActiveTrap *active_traps)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&arena, 1);
|
for(B32 done = 0; !done;)
|
||||||
DMN_EventList evts = {0};
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: push any deferred events
|
|
||||||
//
|
|
||||||
{
|
|
||||||
for EachNode(n, DMN_EventNode, dmn_lnx_state->deferred_events.first)
|
|
||||||
{
|
|
||||||
DMN_Event *e_src = &n->v;
|
|
||||||
DMN_Event *e_dst = dmn_event_list_push(arena, &evts);
|
|
||||||
MemoryCopyStruct(e_dst, e_src);
|
|
||||||
e_dst->string = str8_copy(arena, e_dst->string);
|
|
||||||
}
|
|
||||||
MemoryZeroStruct(&dmn_lnx_state->deferred_events);
|
|
||||||
arena_clear(dmn_lnx_state->deferred_events_arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: no processes, no output events -> not attached
|
|
||||||
//
|
|
||||||
if(evts.count == 0 && dmn_lnx_state->entities_base->first == &dmn_lnx_nil_entity)
|
|
||||||
{
|
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
|
||||||
e->kind = DMN_EventKind_Error;
|
|
||||||
e->error_kind = DMN_ErrorKind_NotAttached;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: determine if we need to wait for new events
|
|
||||||
//
|
|
||||||
B32 need_wait_on_events = (evts.count == 0);
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: write all traps into memory
|
|
||||||
//
|
|
||||||
DMN_ActiveTrap *active_trap_first = 0, *active_trap_last = 0;
|
|
||||||
{
|
|
||||||
ProfScope("write all traps into memory")
|
|
||||||
{
|
|
||||||
HashTable *ht = hash_table_init(scratch.arena, ctrls->traps.trap_count);
|
|
||||||
for EachNode(n, DMN_TrapChunkNode, ctrls->traps.first)
|
|
||||||
{
|
|
||||||
for EachIndex(n_idx, n->count)
|
|
||||||
{
|
|
||||||
DMN_Trap *trap = n->v+n_idx;
|
|
||||||
|
|
||||||
if(trap->flags == 0)
|
|
||||||
{
|
|
||||||
DMN_ActiveTrap *is_set = hash_table_search_u64_raw(ht, trap->vaddr);
|
|
||||||
if(is_set) {continue;}
|
|
||||||
|
|
||||||
U8 swap_byte = 0;
|
|
||||||
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr+1), &swap_byte) > 0)
|
|
||||||
{
|
|
||||||
U8 int3 = 0xCC;
|
|
||||||
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr+1), &int3))
|
|
||||||
{
|
|
||||||
DMN_ActiveTrap *active_trap = push_array(scratch.arena, DMN_ActiveTrap, 1);
|
|
||||||
active_trap->trap = trap;
|
|
||||||
active_trap->swap_byte = swap_byte;
|
|
||||||
SLLQueuePush(active_trap_first, active_trap_last, active_trap);
|
|
||||||
|
|
||||||
hash_table_push_u64_raw(scratch.arena, ht, trap->vaddr, active_trap);
|
|
||||||
} else { Assert(0 && "failed to write trap"); }
|
|
||||||
} else { Assert(0 && "failed to read original byte"); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: gather all threads which we should run
|
|
||||||
//
|
|
||||||
DMN_LNX_EntityNode *first_run_thread = 0, *last_run_thread = 0;
|
|
||||||
if(need_wait_on_events) ProfScope("gather all threads which we should run")
|
|
||||||
{
|
|
||||||
//- rjf: scan all processes
|
|
||||||
for(DMN_LNX_Entity *process = dmn_lnx_state->entities_base->first;
|
|
||||||
process != &dmn_lnx_nil_entity;
|
|
||||||
process = process->next)
|
|
||||||
{
|
|
||||||
if(process->kind != DMN_LNX_EntityKind_Process) {continue;}
|
|
||||||
|
|
||||||
//- rjf: determine if this process is frozen
|
|
||||||
B32 process_is_frozen = 0;
|
|
||||||
if(ctrls->run_entities_are_processes)
|
|
||||||
{
|
|
||||||
for(U64 idx = 0; idx < ctrls->run_entity_count; idx += 1)
|
|
||||||
{
|
|
||||||
if(dmn_handle_match(ctrls->run_entities[idx], dmn_lnx_handle_from_entity(process)))
|
|
||||||
{
|
|
||||||
process_is_frozen = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//- rjf: scan all threads in this process
|
|
||||||
for(DMN_LNX_Entity *thread = process->first;
|
|
||||||
thread != &dmn_lnx_nil_entity;
|
|
||||||
thread = thread->next)
|
|
||||||
{
|
|
||||||
if(thread->kind != DMN_LNX_EntityKind_Thread) {continue;}
|
|
||||||
|
|
||||||
//- rjf: determine if this thread is frozen
|
|
||||||
B32 is_frozen = 0;
|
|
||||||
{
|
|
||||||
// rjf: single-step? freeze if not the single-step thread.
|
|
||||||
if(!dmn_handle_match(dmn_handle_zero(), ctrls->single_step_thread))
|
|
||||||
{
|
|
||||||
is_frozen = !dmn_handle_match(dmn_lnx_handle_from_entity(thread), ctrls->single_step_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
// rjf: not single-stepping? determine based on run controls freezing info
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(ctrls->run_entities_are_processes)
|
|
||||||
{
|
|
||||||
is_frozen = process_is_frozen;
|
|
||||||
}
|
|
||||||
else for(U64 idx = 0; idx < ctrls->run_entity_count; idx += 1)
|
|
||||||
{
|
|
||||||
if(dmn_handle_match(ctrls->run_entities[idx], dmn_lnx_handle_from_entity(thread)))
|
|
||||||
{
|
|
||||||
is_frozen = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ctrls->run_entities_are_unfrozen)
|
|
||||||
{
|
|
||||||
is_frozen ^= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//- rjf: disregard all other rules if this is the halter thread
|
|
||||||
// TODO(rjf): halting - here is what we do on windows...
|
|
||||||
#if 0
|
|
||||||
if(dmn_w32_shared->halter_tid == thread->id)
|
|
||||||
{
|
|
||||||
is_frozen = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//- rjf: add to list
|
|
||||||
if(!is_frozen)
|
|
||||||
{
|
|
||||||
DMN_LNX_EntityNode *n = push_array(scratch.arena, DMN_LNX_EntityNode, 1);
|
|
||||||
n->v = thread;
|
|
||||||
SLLQueuePush(first_run_thread, last_run_thread, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable single stepping
|
|
||||||
DMN_LNX_Entity *single_step_thread = dmn_lnx_entity_from_handle(ctrls->single_step_thread);
|
|
||||||
if(single_step_thread != &dmn_lnx_nil_entity)
|
|
||||||
{
|
|
||||||
dmn_lnx_set_single_step_flag(single_step_thread, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update registers
|
|
||||||
for EachNode(n, DMN_LNX_EntityNode, first_run_thread)
|
|
||||||
{
|
|
||||||
DMN_LNX_Entity *thread = n->v;
|
|
||||||
if(thread->reg_block)
|
|
||||||
{
|
|
||||||
if (!dmn_lnx_thread_write_reg_block(thread, thread->reg_block)) { Assert(0 && "failed to write thread's registers"); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: resume all threads we need to run
|
|
||||||
//
|
|
||||||
DMN_LNX_EntityNode *first_ran_thread = 0, *last_ran_thread = 0;
|
|
||||||
for EachNode(n, DMN_LNX_EntityNode, first_run_thread)
|
|
||||||
{
|
|
||||||
DMN_LNX_Entity *thread = n->v;
|
|
||||||
|
|
||||||
void *sig_code = 0;
|
|
||||||
if(dmn_lnx_state->last_event_kind == DMN_EventKind_Exception && dmn_lnx_state->last_stop_pid == thread->id)
|
|
||||||
{
|
|
||||||
sig_code = (void *)(uintptr_t)dmn_lnx_state->last_sig_code;
|
|
||||||
}
|
|
||||||
if (ptrace(PTRACE_CONT, (pid_t)thread->id, 0, (void *)sig_code) < 0) { Assert(0 && "failed to resume a thread"); }
|
|
||||||
|
|
||||||
DMN_LNX_EntityNode *n2 = push_array_no_zero(scratch.arena, DMN_LNX_EntityNode, 1);
|
|
||||||
SLLQueuePush(first_ran_thread, last_ran_thread, n2);
|
|
||||||
n2->v = thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
|
||||||
//- rjf: loop: wait for next stop, produce debug events
|
|
||||||
//
|
|
||||||
for(B32 done = !need_wait_on_events; !done;)
|
|
||||||
{
|
{
|
||||||
//- rjf: wait for next event
|
//- rjf: wait for next event
|
||||||
int status = 0;
|
int status = 0;
|
||||||
pid_t wait_id = waitpid(-1, &status, __WALL|__WNOTHREAD);
|
pid_t wait_id = waitpid(tid, &status, __WALL|__WNOTHREAD);
|
||||||
if(status == -1 && errno == EINTR) {continue;} // wait interrupted, try again
|
if(status == -1 && errno == EINTR) {continue;} // wait interrupted, try again
|
||||||
if(status == -1) {InvalidPath;} // TODO: graceful exit
|
if(status == -1) {InvalidPath;} // TODO: graceful exit
|
||||||
|
|
||||||
@@ -2144,7 +1977,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
int ptrace_event_code = (status>>16);
|
int ptrace_event_code = (status>>16);
|
||||||
DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(wait_id);
|
DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(wait_id);
|
||||||
DMN_LNX_Entity *process = thread->parent;
|
DMN_LNX_Entity *process = thread->parent;
|
||||||
B32 thread_is_process_root = (thread->id == process->id);
|
|
||||||
|
//printf("waitpid - pid %d wifexited %d, wifsignaled %d, wifstopped %d, wstopsig %d\n", wait_id, wifexited, wifsignaled, wifstopped, wstopsig);
|
||||||
|
|
||||||
// update thread registers
|
// update thread registers
|
||||||
if(thread != &dmn_lnx_nil_entity)
|
if(thread != &dmn_lnx_nil_entity)
|
||||||
@@ -2156,6 +1990,8 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
U64 exit_code = max_U64;
|
U64 exit_code = max_U64;
|
||||||
U64 address = 0;
|
U64 address = 0;
|
||||||
DMN_Trap *hit_user_trap = 0;
|
DMN_Trap *hit_user_trap = 0;
|
||||||
|
pid_t new_pid = 0;
|
||||||
|
B32 is_group_stop = 0;
|
||||||
|
|
||||||
//- rjf: WIFEXITED(status) -> thread exit
|
//- rjf: WIFEXITED(status) -> thread exit
|
||||||
if(wifexited)
|
if(wifexited)
|
||||||
@@ -2178,9 +2014,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//- rjf: SIGTRAP:PTRACE_EVENT_CLONE
|
//- rjf: SIGTRAP:PTRACE_EVENT_CLONE
|
||||||
else if(wifstopped && wstopsig == SIGTRAP && ptrace_event_code == PTRACE_EVENT_CLONE)
|
else if(wifstopped && (status >> 8) == (SIGTRAP | PTRACE_EVENT_CLONE << 8))
|
||||||
{
|
{
|
||||||
// TODO(rjf)
|
if(ptrace(PTRACE_GETEVENTMSG, wait_id, 0, &new_pid) >= 0)
|
||||||
|
{
|
||||||
|
e_kind = DMN_EventKind_CreateThread;
|
||||||
|
}
|
||||||
|
else { Assert(0 && "failed to get new tid"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
//- rjf: SIGTRAP:PTRACE_EVENT_FORK, or SIGTRAP:PTRACE_EVENT_VFORK
|
//- rjf: SIGTRAP:PTRACE_EVENT_FORK, or SIGTRAP:PTRACE_EVENT_VFORK
|
||||||
@@ -2190,12 +2030,19 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// group stop
|
||||||
|
else if(wifstopped && wstopsig == SIGTRAP && status >> 16 == PTRACE_EVENT_STOP)
|
||||||
|
{
|
||||||
|
is_group_stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
//- rjf: SIGTRAP
|
//- rjf: SIGTRAP
|
||||||
else if(wifstopped && wstopsig == SIGTRAP)
|
else if(wifstopped && wstopsig == SIGTRAP)
|
||||||
{
|
{
|
||||||
// translate signal code to DEMON event kind
|
// translate signal code to DEMON event kind
|
||||||
siginfo_t siginfo = {0};
|
siginfo_t siginfo = {0};
|
||||||
if(ptrace(PTRACE_GETSIGINFO, wait_id, 0, &siginfo) < 0) { Assert(0 && "failed to get signal info"); }
|
if(ptrace(PTRACE_GETSIGINFO, wait_id, 0, &siginfo) < 0) { Assert(0 && "failed to get signal info"); }
|
||||||
|
|
||||||
switch(siginfo.si_code)
|
switch(siginfo.si_code)
|
||||||
{
|
{
|
||||||
case DMN_LNX_SigTrapCode_Brkpt:
|
case DMN_LNX_SigTrapCode_Brkpt:
|
||||||
@@ -2292,12 +2139,23 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
dmn_lnx_state->last_event_kind = e_kind;
|
dmn_lnx_state->last_event_kind = e_kind;
|
||||||
dmn_lnx_state->last_stop_pid = wait_id;
|
dmn_lnx_state->last_stop_pid = wait_id;
|
||||||
dmn_lnx_state->last_sig_code = wstopsig;
|
dmn_lnx_state->last_sig_code = wstopsig;
|
||||||
|
|
||||||
|
if(wait_for_group_stop)
|
||||||
|
{
|
||||||
|
if(is_group_stop)
|
||||||
|
{
|
||||||
done = 1;
|
done = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if(e_kind == DMN_EventKind_Breakpoint)
|
if(e_kind == DMN_EventKind_Breakpoint)
|
||||||
{
|
{
|
||||||
U64 ip = dmn_lnx_thread_read_ip(thread);
|
U64 ip = dmn_lnx_thread_read_ip(thread);
|
||||||
for EachNode(active_trap, DMN_ActiveTrap, active_trap_first)
|
for EachNode(active_trap, DMN_ActiveTrap, active_traps)
|
||||||
{
|
{
|
||||||
if(active_trap->trap->vaddr == ip-1)
|
if(active_trap->trap->vaddr == ip-1)
|
||||||
{
|
{
|
||||||
@@ -2335,7 +2193,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
{
|
{
|
||||||
if(rdebug.r_version == 1)
|
if(rdebug.r_version == 1)
|
||||||
{
|
{
|
||||||
dmn_lnx_process_loaded_modules(arena, &evts, process, name_space_id, rdebug.r_map);
|
dmn_lnx_process_loaded_modules(arena, evts, process, name_space_id, rdebug.r_map);
|
||||||
}
|
}
|
||||||
else { Assert(0 && "unexpected version number"); }
|
else { Assert(0 && "unexpected version number"); }
|
||||||
}
|
}
|
||||||
@@ -2353,7 +2211,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
{
|
{
|
||||||
if(stap_read_arg_u(probe->args.v[2], process->arch, thread->reg_block, dmn_lnx_stap_memory_read, process, &new_link_map_addr))
|
if(stap_read_arg_u(probe->args.v[2], process->arch, thread->reg_block, dmn_lnx_stap_memory_read, process, &new_link_map_addr))
|
||||||
{
|
{
|
||||||
dmn_lnx_process_loaded_modules(arena, &evts, process, name_space_id, new_link_map_addr);
|
dmn_lnx_process_loaded_modules(arena, evts, process, name_space_id, new_link_map_addr);
|
||||||
}
|
}
|
||||||
else { Assert(0 && "failed to parse third argument"); }
|
else { Assert(0 && "failed to parse third argument"); }
|
||||||
}
|
}
|
||||||
@@ -2368,7 +2226,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
{
|
{
|
||||||
if(stap_read_arg_u(probe->args.v[1], process->arch, thread->reg_block, dmn_lnx_stap_memory_read, process, &rdebug_vaddr))
|
if(stap_read_arg_u(probe->args.v[1], process->arch, thread->reg_block, dmn_lnx_stap_memory_read, process, &rdebug_vaddr))
|
||||||
{
|
{
|
||||||
dmn_lnx_process_unloaded_modules(arena, &evts, process, name_space_id, rdebug_vaddr);
|
dmn_lnx_process_unloaded_modules(arena, evts, process, name_space_id, rdebug_vaddr);
|
||||||
}
|
}
|
||||||
else { Assert(0 && "failed to read second argument"); }
|
else { Assert(0 && "failed to read second argument"); }
|
||||||
}
|
}
|
||||||
@@ -2410,7 +2268,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
// clear single step flag
|
// clear single step flag
|
||||||
dmn_lnx_set_single_step_flag(thread, 0);
|
dmn_lnx_set_single_step_flag(thread, 0);
|
||||||
|
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = e_kind;
|
e->kind = e_kind;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
@@ -2418,7 +2276,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}break;
|
}break;
|
||||||
case DMN_EventKind_Breakpoint:
|
case DMN_EventKind_Breakpoint:
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = e_kind;
|
e->kind = e_kind;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
@@ -2426,7 +2284,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}break;
|
}break;
|
||||||
case DMN_EventKind_Halt:
|
case DMN_EventKind_Halt:
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_Halt;
|
e->kind = DMN_EventKind_Halt;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
@@ -2438,7 +2296,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
// SIGFPE
|
// SIGFPE
|
||||||
// SIGSEGV
|
// SIGSEGV
|
||||||
// SIGILL
|
// SIGILL
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_Exception;
|
e->kind = DMN_EventKind_Exception;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
@@ -2459,14 +2317,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
default:{}break;
|
default:{}break;
|
||||||
case DMN_LNX_EntityKind_Thread:
|
case DMN_LNX_EntityKind_Thread:
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_ExitThread;
|
e->kind = DMN_EventKind_ExitThread;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(child);
|
e->thread = dmn_lnx_handle_from_entity(child);
|
||||||
}break;
|
}break;
|
||||||
case DMN_LNX_EntityKind_Module:
|
case DMN_LNX_EntityKind_Module:
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_UnloadModule;
|
e->kind = DMN_EventKind_UnloadModule;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->module = dmn_lnx_handle_from_entity(child);
|
e->module = dmn_lnx_handle_from_entity(child);
|
||||||
@@ -2477,7 +2335,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
|
|
||||||
// rjf: generate exit process event
|
// rjf: generate exit process event
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_ExitProcess;
|
e->kind = DMN_EventKind_ExitProcess;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->code = exit_code;
|
e->code = exit_code;
|
||||||
@@ -2488,11 +2346,22 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}break;
|
}break;
|
||||||
case DMN_EventKind_CreateThread:
|
case DMN_EventKind_CreateThread:
|
||||||
{
|
{
|
||||||
NotImplemented;
|
DMN_LNX_Entity *thread = dmn_lnx_entity_alloc(process, DMN_LNX_EntityKind_Thread);
|
||||||
|
thread->id = new_pid;
|
||||||
|
thread->arch = process->arch;
|
||||||
|
thread->reg_block = push_array(process->arena, U8, regs_block_size_from_arch(process->arch));
|
||||||
|
dmn_lnx_thread_read_reg_block(thread, thread->reg_block);
|
||||||
|
|
||||||
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
|
e->kind = DMN_EventKind_CreateThread;
|
||||||
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
|
e->arch = thread->arch;
|
||||||
|
e->code = thread->id;
|
||||||
}break;
|
}break;
|
||||||
case DMN_EventKind_ExitThread:
|
case DMN_EventKind_ExitThread:
|
||||||
{
|
{
|
||||||
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
DMN_Event *e = dmn_event_list_push(arena, evts);
|
||||||
e->kind = DMN_EventKind_ExitThread;
|
e->kind = DMN_EventKind_ExitThread;
|
||||||
e->process = dmn_lnx_handle_from_entity(process);
|
e->process = dmn_lnx_handle_from_entity(process);
|
||||||
e->thread = dmn_lnx_handle_from_entity(thread);
|
e->thread = dmn_lnx_handle_from_entity(thread);
|
||||||
@@ -2500,20 +2369,220 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
}break;
|
}break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal DMN_EventList
|
||||||
|
dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
DMN_EventList evts = {0};
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: push any deferred events
|
||||||
|
//
|
||||||
|
{
|
||||||
|
for EachNode(n, DMN_EventNode, dmn_lnx_state->deferred_events.first)
|
||||||
|
{
|
||||||
|
DMN_Event *e_src = &n->v;
|
||||||
|
DMN_Event *e_dst = dmn_event_list_push(arena, &evts);
|
||||||
|
MemoryCopyStruct(e_dst, e_src);
|
||||||
|
e_dst->string = str8_copy(arena, e_dst->string);
|
||||||
|
}
|
||||||
|
MemoryZeroStruct(&dmn_lnx_state->deferred_events);
|
||||||
|
arena_clear(dmn_lnx_state->deferred_events_arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: no processes, no output events -> not attached
|
||||||
|
//
|
||||||
|
if(evts.count == 0 && dmn_lnx_state->entities_base->first == &dmn_lnx_nil_entity)
|
||||||
|
{
|
||||||
|
DMN_Event *e = dmn_event_list_push(arena, &evts);
|
||||||
|
e->kind = DMN_EventKind_Error;
|
||||||
|
e->error_kind = DMN_ErrorKind_NotAttached;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: determine if we need to wait for new events
|
||||||
|
//
|
||||||
|
B32 need_wait_on_events = (evts.count == 0);
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: write all traps into memory
|
||||||
|
//
|
||||||
|
DMN_ActiveTrap *active_trap_first = 0, *active_trap_last = 0;
|
||||||
|
ProfScope("write all traps into memory")
|
||||||
|
{
|
||||||
|
HashTable *ht = hash_table_init(scratch.arena, ctrls->traps.trap_count);
|
||||||
|
for EachNode(n, DMN_TrapChunkNode, ctrls->traps.first)
|
||||||
|
{
|
||||||
|
for EachIndex(n_idx, n->count)
|
||||||
|
{
|
||||||
|
DMN_Trap *trap = n->v+n_idx;
|
||||||
|
|
||||||
|
if(trap->flags == 0)
|
||||||
|
{
|
||||||
|
DMN_ActiveTrap *is_set = hash_table_search_u64_raw(ht, trap->vaddr);
|
||||||
|
if(is_set) { continue; }
|
||||||
|
|
||||||
|
U8 swap_byte = 0;
|
||||||
|
if(dmn_process_read(trap->process, r1u64(trap->vaddr, trap->vaddr+1), &swap_byte) > 0)
|
||||||
|
{
|
||||||
|
U8 int3 = 0xCC;
|
||||||
|
if(dmn_process_write(trap->process, r1u64(trap->vaddr, trap->vaddr+1), &int3))
|
||||||
|
{
|
||||||
|
DMN_ActiveTrap *active_trap = push_array(scratch.arena, DMN_ActiveTrap, 1);
|
||||||
|
active_trap->trap = trap;
|
||||||
|
active_trap->swap_byte = swap_byte;
|
||||||
|
SLLQueuePush(active_trap_first, active_trap_last, active_trap);
|
||||||
|
|
||||||
|
hash_table_push_u64_raw(scratch.arena, ht, trap->vaddr, active_trap);
|
||||||
|
} else { Assert(0 && "failed to write trap"); }
|
||||||
|
} else { Assert(0 && "failed to read original byte"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- enable single stepping
|
||||||
|
if(!dmn_handle_match(ctrls->single_step_thread, dmn_handle_zero()))
|
||||||
|
{
|
||||||
|
DMN_LNX_Entity *single_step_thread = dmn_lnx_entity_from_handle(ctrls->single_step_thread);
|
||||||
|
dmn_lnx_set_single_step_flag(single_step_thread, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: gather all threads which we should run
|
||||||
|
//
|
||||||
|
DMN_LNX_EntityList run_threads = {0};
|
||||||
|
if(need_wait_on_events) ProfScope("gather all threads which we should run")
|
||||||
|
{
|
||||||
|
//- rjf: scan all processes
|
||||||
|
for(DMN_LNX_Entity *process = dmn_lnx_state->entities_base->first; process != &dmn_lnx_nil_entity; process = process->next)
|
||||||
|
{
|
||||||
|
if(process->kind != DMN_LNX_EntityKind_Process) { continue; }
|
||||||
|
|
||||||
|
//- rjf: determine if this process is frozen
|
||||||
|
B32 process_is_frozen = 0;
|
||||||
|
if(ctrls->run_entities_are_processes)
|
||||||
|
{
|
||||||
|
for EachIndex(idx, ctrls->run_entity_count)
|
||||||
|
{
|
||||||
|
if(dmn_handle_match(ctrls->run_entities[idx], dmn_lnx_handle_from_entity(process)))
|
||||||
|
{
|
||||||
|
process_is_frozen = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- rjf: scan all threads in this process
|
||||||
|
for(DMN_LNX_Entity *thread = process->first; thread != &dmn_lnx_nil_entity; thread = thread->next)
|
||||||
|
{
|
||||||
|
if(thread->kind != DMN_LNX_EntityKind_Thread) { continue; }
|
||||||
|
|
||||||
|
//- rjf: determine if this thread is frozen
|
||||||
|
B32 is_frozen = 0;
|
||||||
|
{
|
||||||
|
// rjf: single-step? freeze if not the single-step thread.
|
||||||
|
if(!dmn_handle_match(dmn_handle_zero(), ctrls->single_step_thread))
|
||||||
|
{
|
||||||
|
is_frozen = !dmn_handle_match(dmn_lnx_handle_from_entity(thread), ctrls->single_step_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rjf: not single-stepping? determine based on run controls freezing info
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(ctrls->run_entities_are_processes)
|
||||||
|
{
|
||||||
|
is_frozen = process_is_frozen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for EachIndex(idx, ctrls->run_entity_count)
|
||||||
|
{
|
||||||
|
if(dmn_handle_match(ctrls->run_entities[idx], dmn_lnx_handle_from_entity(thread)))
|
||||||
|
{
|
||||||
|
is_frozen = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ctrls->run_entities_are_unfrozen)
|
||||||
|
{
|
||||||
|
is_frozen ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- rjf: add to list
|
||||||
|
if(!is_frozen)
|
||||||
|
{
|
||||||
|
dmn_lnx_entity_list_push(scratch.arena, &run_threads, thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: resume all threads we need to run
|
||||||
|
//
|
||||||
|
DMN_LNX_EntityList ran_threads = {0};
|
||||||
|
for EachNode(n, DMN_LNX_EntityNode, run_threads.first)
|
||||||
|
{
|
||||||
|
DMN_LNX_Entity *thread = n->v;
|
||||||
|
|
||||||
|
// update registers
|
||||||
|
if(!dmn_lnx_thread_write_reg_block(thread, thread->reg_block)) { Assert(0 && "failed to write thread's registers"); }
|
||||||
|
|
||||||
|
// pass signal to the child process
|
||||||
|
void *sig_code = 0;
|
||||||
|
if(dmn_lnx_state->last_event_kind == DMN_EventKind_Exception && dmn_lnx_state->last_stop_pid == thread->id)
|
||||||
|
{
|
||||||
|
sig_code = (void *)(uintptr_t)dmn_lnx_state->last_sig_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume thread
|
||||||
|
if (ptrace(PTRACE_CONT, (pid_t)thread->id, 0, (void *)sig_code) < 0) { Assert(0 && "failed to resume a thread"); }
|
||||||
|
|
||||||
|
dmn_lnx_entity_list_push(scratch.arena, &ran_threads, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//- rjf: loop: wait for next stop, produce debug events
|
||||||
|
//
|
||||||
|
if(need_wait_on_events)
|
||||||
|
{
|
||||||
|
dmn_lnx_wait_for_events(arena, &evts, -1, 0, active_trap_first);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
//- rjf: stop all threads
|
//- rjf: stop all threads
|
||||||
//
|
//
|
||||||
for EachNode(n, DMN_LNX_EntityNode, first_ran_thread)
|
|
||||||
{
|
{
|
||||||
DMN_LNX_Entity *thread = n->v;
|
B32 was_interrupt_issued = 0;
|
||||||
pid_t thread_id = (pid_t)thread->id;
|
for EachNode(n, DMN_LNX_EntityNode, ran_threads.first)
|
||||||
if(thread_id != dmn_lnx_state->last_stop_pid)
|
|
||||||
{
|
{
|
||||||
union sigval sv = {0};
|
if(n->v->id != dmn_lnx_state->last_stop_pid)
|
||||||
sigqueue(thread_id, SIGSTOP, sv);
|
{
|
||||||
thread->expecting_dummy_sigstop = 1;
|
if(ptrace(PTRACE_INTERRUPT, n->v->id, 0, 0) >= 0)
|
||||||
|
{
|
||||||
|
was_interrupt_issued = 1;
|
||||||
}
|
}
|
||||||
|
else { Assert(0 && "failed to interrupt thread"); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(was_interrupt_issued)
|
||||||
|
{
|
||||||
|
dmn_lnx_wait_for_events(arena, &evts, -1, 1, active_trap_first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update registers
|
||||||
|
for EachNode(n, DMN_LNX_EntityNode, ran_threads.first)
|
||||||
|
{
|
||||||
|
dmn_lnx_thread_read_reg_block(n->v, n->v->reg_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
@@ -2525,7 +2594,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
|
|||||||
{
|
{
|
||||||
if(!dmn_process_write_struct(active_trap->trap->process, active_trap->trap->vaddr, &active_trap->swap_byte))
|
if(!dmn_process_write_struct(active_trap->trap->process, active_trap->trap->vaddr, &active_trap->swap_byte))
|
||||||
{
|
{
|
||||||
Assert(0 && "failed to swap back original byte");
|
Assert(0 && "failed to restore original memory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user