demon/linux: sketch out signal interpretation, more ptrace study

This commit is contained in:
Ryan Fleury
2025-07-28 17:25:24 -07:00
parent 122c27eabe
commit 64c2d98ede
2 changed files with 178 additions and 4 deletions
+176 -4
View File
@@ -464,7 +464,28 @@ dmn_lnx_entity_alloc(DMN_LNX_Entity *parent, DMN_LNX_EntityKind kind)
internal void
dmn_lnx_entity_release(DMN_LNX_Entity *entity)
{
SLLStackPush(dmn_lnx_state->free_entity, entity);
if(entity->parent != &dmn_lnx_nil_entity)
{
DLLRemove_NPZ(&dmn_lnx_nil_entity, entity->parent->first, entity->parent->last, entity, next, prev);
entity->parent = &dmn_lnx_nil_entity;
}
{
Temp scratch = scratch_begin(0, 0);
DMN_LNX_EntityNode start_task = {0, entity};
DMN_LNX_EntityNode *first_task = &start_task;
for(DMN_LNX_EntityNode *t = first_task; t != 0; t = t->next)
{
SLLStackPush(dmn_lnx_state->free_entity, t->v);
for(DMN_LNX_Entity *child = t->v->first; child != &dmn_lnx_nil_entity; child = child->next)
{
DMN_LNX_EntityNode *task = push_array(scratch.arena, DMN_LNX_EntityNode, 1);
task->next = t->next;
t->next = task;
task->v = child;
}
}
scratch_end(scratch);
}
}
internal DMN_Handle
@@ -493,6 +514,24 @@ dmn_lnx_entity_from_handle(DMN_Handle handle)
return result;
}
internal DMN_LNX_Entity *
dmn_lnx_thread_from_pid(pid_t pid)
{
DMN_LNX_Entity *result = &dmn_lnx_nil_entity;
if(pid != 0)
{
for EachIndex(idx, dmn_lnx_state->entities_count)
{
if(dmn_lnx_state->entities_base[idx].kind == DMN_LNX_EntityKind_Thread && (pid_t)dmn_lnx_state->entities_base[idx].id == pid)
{
result = &dmn_lnx_state->entities_base[idx];
break;
}
}
}
return result;
}
////////////////////////////////
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
@@ -914,11 +953,144 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
}
////////////////////////////
//- rjf: wait for next stop
//- rjf: loop: wait for next stop, produce debug events
//
int status = 0;
pid_t wait_id = waitpid(-1, &status, __WALL);
pid_t final_wait_pid = 0;
for(B32 done = 0; !done;)
{
//- rjf: wait for next event
int status = 0;
pid_t wait_id = waitpid(-1, &status, __WALL);
final_wait_pid = wait_id;
//- rjf: unpack event
int wifexited = WIFEXITED(status);
int wifsignaled = WIFSIGNALED(status);
int wifstopped = WIFSTOPPED(status);
int wstopsig = WSTOPSIG(status);
int ptrace_event_code = (status>>8);
DMN_LNX_Entity *thread = dmn_lnx_thread_from_pid(wait_id);
DMN_LNX_Entity *process = thread->parent;
B32 thread_is_process_root = (thread->id == process->id);
//- rjf: WIFEXITED(status) -> thread exit
B32 thread_exit = 0;
U64 exit_code = 0;
if(wifexited)
{
thread_exit = 1;
}
//- rjf: WIFEXITED(status) -> thread exit w/ exit code
if(wifsignaled)
{
exit_code = WTERMSIG(status);
thread_exit = 1;
}
//- rjf: SIGTRAP:PTRACE_EVENT_EXIT
if(wifstopped && wstopsig == SIGTRAP && ptrace_event_code == PTRACE_EVENT_EXIT)
{
// TODO(rjf): verify
thread_exit = 1;
}
//- rjf: SIGTRAP:PTRACE_EVENT_CLONE
if(wifstopped && wstopsig == SIGTRAP && ptrace_event_code == PTRACE_EVENT_CLONE)
{
// TODO(rjf)
}
//- rjf: SIGTRAP:PTRACE_EVENT_FORK, or SIGTRAP:PTRACE_EVENT_VFORK
if(wifstopped && wstopsig == SIGTRAP &&
(ptrace_event_code == PTRACE_EVENT_FORK ||
ptrace_event_code == PTRACE_EVENT_VFORK))
{
// TODO(rjf)
}
//- rjf: WSTOPSIG(status) is SIGSTOP
if(wifstopped && wstopsig == SIGSTOP)
{
// TODO(rjf): how do we tell the following apart?:
// - SIGSTOP All-Stop
// - SIGSTOP Halt
// - SIGSTOP "User"
}
//- rjf: WSTOPSIG(status) is an unrecoverable exception (unless user does something to fix state first)
if(wifstopped &&
(wstopsig == SIGABRT ||
wstopsig == SIGFPE ||
wstopsig == SIGSEGV))
{
// TODO(rjf)
}
//- rjf: thread exit, thread is process' "root thread" -> eliminate this entire entity subtree
if(thread_exit && thread_is_process_root)
{
// rjf: generate exit-thread / unload-module events
for(DMN_LNX_Entity *child = process->first; child != &dmn_lnx_nil_entity; child = child->next)
{
switch(child->kind)
{
default:{}break;
case DMN_LNX_EntityKind_Thread:
{
DMN_Event *e = dmn_event_list_push(arena, &evts);
e->kind = DMN_EventKind_ExitThread;
e->process = dmn_lnx_handle_from_entity(process);
e->thread = dmn_lnx_handle_from_entity(child);
}break;
case DMN_LNX_EntityKind_Module:
{
DMN_Event *e = dmn_event_list_push(arena, &evts);
e->kind = DMN_EventKind_UnloadModule;
e->process = dmn_lnx_handle_from_entity(process);
e->module = dmn_lnx_handle_from_entity(child);
// TODO(rjf): e->string = ...;
}break;
}
}
// rjf: generate exit process event
{
DMN_Event *e = dmn_event_list_push(arena, &evts);
e->kind = DMN_EventKind_ExitProcess;
e->process = dmn_lnx_handle_from_entity(process);
e->code = exit_code;
}
// rjf: eliminate entity tree
dmn_lnx_entity_release(process);
}
//- rjf: thread exit, thread is *not* process root -> just exit this one thread
if(thread_exit && !thread_is_process_root)
{
DMN_Event *e = dmn_event_list_push(arena, &evts);
e->kind = DMN_EventKind_ExitThread;
e->process = dmn_lnx_handle_from_entity(process);
e->thread = dmn_lnx_handle_from_entity(thread);
dmn_lnx_entity_release(thread);
}
}
////////////////////////////
//- rjf: stop all threads
//
for(DMN_LNX_EntityNode *n = first_ran_thread; n != 0; n = n->next)
{
DMN_LNX_Entity *thread = n->v;
pid_t thread_id = (pid_t)thread->id;
if(thread_id != final_wait_pid)
{
union sigval sv = {0};
sigqueue(thread_id, SIGSTOP, sv);
thread->expecting_dummy_sigstop = 1;
}
}
scratch_end(scratch);
}
+2
View File
@@ -81,6 +81,7 @@ struct DMN_LNX_Entity
Arch arch;
U64 id;
int fd;
B32 expecting_dummy_sigstop;
};
typedef struct DMN_LNX_EntityNode DMN_LNX_EntityNode;
@@ -142,5 +143,6 @@ internal DMN_LNX_Entity *dmn_lnx_entity_alloc(DMN_LNX_Entity *parent, DMN_LNX_En
internal void dmn_lnx_entity_release(DMN_LNX_Entity *entity);
internal DMN_Handle dmn_lnx_handle_from_entity(DMN_LNX_Entity *entity);
internal DMN_LNX_Entity *dmn_lnx_entity_from_handle(DMN_Handle handle);
internal DMN_LNX_Entity *dmn_lnx_thread_from_pid(pid_t pid);
#endif // DEMON_CORE_LINUX_H