From 62659c5161ce25a671b6585726f2cbaa531f4f54 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Wed, 23 Oct 2024 13:19:37 -0700 Subject: [PATCH] introduce dedicated kill-all path; fix exit conditions for single-stepping stuck threads to be robust toward unexpected kills; fix exit conditions for kill-single-process path, to be more robust to halts; correctly report stop cause --- src/ctrl/ctrl_core.c | 120 +++++++++++++++++++++++++++---- src/ctrl/ctrl_core.h | 2 + src/dbg_engine/dbg_engine_core.c | 23 +++--- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index ab1f3460..03c60e83 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -81,6 +81,7 @@ ctrl_string_from_msg_kind(CTRL_MsgKind kind) case CTRL_MsgKind_Launch: {result = str8_lit("Launch");}break; case CTRL_MsgKind_Attach: {result = str8_lit("Attach");}break; case CTRL_MsgKind_Kill: {result = str8_lit("Kill");}break; + case CTRL_MsgKind_KillAll: {result = str8_lit("KillAll");}break; case CTRL_MsgKind_Detach: {result = str8_lit("Detach");}break; case CTRL_MsgKind_Run: {result = str8_lit("Run");}break; case CTRL_MsgKind_SingleStep: {result = str8_lit("SingleStep");}break; @@ -3250,8 +3251,7 @@ ctrl_thread__entry_point(void *p) //- rjf: process messages DMN_CtrlExclusiveAccessScope { - B32 done = 0; - for(CTRL_MsgNode *msg_n = msgs.first; msg_n != 0 && done == 0; msg_n = msg_n->next) + for(CTRL_MsgNode *msg_n = msgs.first; msg_n != 0; msg_n = msg_n->next) { CTRL_Msg *msg = &msg_n->v; { @@ -3275,9 +3275,10 @@ ctrl_thread__entry_point(void *p) case CTRL_MsgKind_Launch: {ctrl_thread__launch (ctrl_ctx, msg);}break; case CTRL_MsgKind_Attach: {ctrl_thread__attach (ctrl_ctx, msg);}break; case CTRL_MsgKind_Kill: {ctrl_thread__kill (ctrl_ctx, msg);}break; + case CTRL_MsgKind_KillAll: {ctrl_thread__kill_all (ctrl_ctx, msg);}break; case CTRL_MsgKind_Detach: {ctrl_thread__detach (ctrl_ctx, msg);}break; - case CTRL_MsgKind_Run: {ctrl_thread__run (ctrl_ctx, msg); done = 1;}break; - case CTRL_MsgKind_SingleStep: {ctrl_thread__single_step (ctrl_ctx, msg); done = 1;}break; + case CTRL_MsgKind_Run: {ctrl_thread__run (ctrl_ctx, msg);}break; + case CTRL_MsgKind_SingleStep: {ctrl_thread__single_step (ctrl_ctx, msg);}break; //- rjf: configuration case CTRL_MsgKind_SetUserEntryPoints: @@ -4391,6 +4392,7 @@ ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) B32 kill_worked = dmn_ctrl_kill(ctrl_ctx, process, exit_code); //- rjf: wait for process to be dead + CTRL_EventCause cause = CTRL_EventCause_Finished; if(kill_worked) { DMN_RunCtrls run_ctrls = {0}; @@ -4401,12 +4403,102 @@ ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) for(B32 done = 0; done == 0;) { DMN_Event *event = ctrl_thread__next_dmn_event(scratch.arena, ctrl_ctx, msg, &run_ctrls, 0); - if(event->kind == DMN_EventKind_ExitProcess && dmn_handle_match(event->process, process)) + switch(event->kind) { - done = 1; + default:{}break; + case DMN_EventKind_ExitProcess: + if(dmn_handle_match(event->process, process)) + { + done = 1; + }break; + case DMN_EventKind_Error:{done = 1; cause = CTRL_EventCause_Error;}break; + case DMN_EventKind_Halt: {done = 1; cause = CTRL_EventCause_InterruptedByHalt;}break; } - if(event->kind == DMN_EventKind_Null || - event->kind == DMN_EventKind_Error) + } + } + + //- rjf: record stop + { + CTRL_EventList evts = {0}; + CTRL_Event *event = ctrl_event_list_push(scratch.arena, &evts); + event->kind = CTRL_EventKind_Stopped; + event->cause = cause; + event->msg_id = msg->msg_id; + if(kill_worked) + { + event->entity = msg->entity; + } + ctrl_c2u_push_events(&evts); + } + + scratch_end(scratch); + ProfEnd(); +} + +internal void +ctrl_thread__kill_all(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + U32 exit_code = msg->exit_code; + + //- rjf: gather all currently existing processes + CTRL_EntityList initial_processes = ctrl_entity_list_from_kind(ctrl_state->ctrl_thread_entity_store, CTRL_EntityKind_Process); + typedef struct Task Task; + struct Task + { + Task *next; + Task *prev; + CTRL_Entity *process; + }; + Task *first_task = 0; + Task *last_task = 0; + for(CTRL_EntityNode *n = initial_processes.first; n != 0; n = n->next) + { + Task *t = push_array(scratch.arena, Task, 1); + t->process = n->v; + DLLPushBack(first_task, last_task, t); + } + + //- rjf: kill processes as needed, wait for all processes to be dead + CTRL_EventCause cause = CTRL_EventCause_Finished; + if(first_task != 0) + { + DMN_RunCtrls run_ctrls = {0}; + for(B32 done = 0; !done;) + { + // rjf: kill remaining processes + for(Task *t = first_task, *next = 0; t != 0; t = next) + { + next = t->next; + B32 kill_worked = dmn_ctrl_kill(ctrl_ctx, t->process->handle.dmn_handle, exit_code); + if(kill_worked) + { + DLLRemove(first_task, last_task, t); + } + } + + // rjf: get next event + DMN_Event *event = ctrl_thread__next_dmn_event(scratch.arena, ctrl_ctx, msg, &run_ctrls, 0); + + // rjf: process event + switch(event->kind) + { + default:{}break; + case DMN_EventKind_CreateProcess: + { + CTRL_Entity *new_process = ctrl_entity_from_handle(ctrl_state->ctrl_thread_entity_store, ctrl_handle_make(CTRL_MachineID_Local, event->process)); + Task *t = push_array(scratch.arena, Task, 1); + t->process = new_process; + DLLPushBack(first_task, last_task, t); + }break; + case DMN_EventKind_Error:{done = 1; cause = CTRL_EventCause_Error;}break; + case DMN_EventKind_Halt: {done = 1; cause = CTRL_EventCause_InterruptedByHalt;}break; + } + + // rjf: end if all processes are gone + CTRL_EntityList processes = ctrl_entity_list_from_kind(ctrl_state->ctrl_thread_entity_store, CTRL_EntityKind_Process); + if(processes.count == 0) { done = 1; } @@ -4418,12 +4510,8 @@ ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) CTRL_EventList evts = {0}; CTRL_Event *event = ctrl_event_list_push(scratch.arena, &evts); event->kind = CTRL_EventKind_Stopped; - event->cause = CTRL_EventCause_Finished; + event->cause = cause; event->msg_id = msg->msg_id; - if(kill_worked) - { - event->entity = msg->entity; - } ctrl_c2u_push_events(&evts); } @@ -4623,6 +4711,12 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) switch(event->kind) { default:{}break; + case DMN_EventKind_ExitThread: + if(dmn_handle_match(event->thread, thread)) + { + stop_cause = CTRL_EventCause_Error; + goto stop; + }break; case DMN_EventKind_Error: stop_cause = CTRL_EventCause_Error; goto stop; case DMN_EventKind_Exception: stop_cause = CTRL_EventCause_InterruptedByException; goto stop; case DMN_EventKind_Trap: stop_cause = CTRL_EventCause_InterruptedByTrap; goto stop; diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 1b42d6e3..70f3a29c 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -517,6 +517,7 @@ typedef enum CTRL_MsgKind CTRL_MsgKind_Launch, CTRL_MsgKind_Attach, CTRL_MsgKind_Kill, + CTRL_MsgKind_KillAll, CTRL_MsgKind_Detach, CTRL_MsgKind_Run, CTRL_MsgKind_SingleStep, @@ -1141,6 +1142,7 @@ internal void ctrl_thread__end_and_flush_info_log(void); internal void ctrl_thread__launch(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__attach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); +internal void ctrl_thread__kill_all(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__detach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index f92b520c..c4bae55f 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -2108,16 +2108,11 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P }break; case D_CmdKind_KillAll: { - CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process); - for(CTRL_EntityNode *n = processes.first; n != 0; n = n->next) - { - CTRL_Msg *msg = ctrl_msg_list_push(scratch.arena, &ctrl_msgs); - msg->kind = CTRL_MsgKind_Kill; - msg->exit_code = 1; - msg->entity = n->v->handle; - MemoryCopyArray(msg->exception_code_filters, exception_code_filters); - MemoryCopyStruct(&msg->meta_evals, meta_evals); - } + CTRL_Msg *msg = ctrl_msg_list_push(scratch.arena, &ctrl_msgs); + msg->kind = CTRL_MsgKind_KillAll; + msg->exit_code = 1; + MemoryCopyArray(msg->exception_code_filters, exception_code_filters); + MemoryCopyStruct(&msg->meta_evals, meta_evals); }break; case D_CmdKind_Detach: { @@ -2305,8 +2300,12 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P }break; case D_CmdKind_Restart: { - d_cmd(D_CmdKind_KillAll); - d_cmd(D_CmdKind_LaunchAndRun); + CTRL_EntityList processes = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Process); + if(processes.count != 0) + { + d_cmd(D_CmdKind_KillAll); + d_cmd(D_CmdKind_LaunchAndRun); + } }break; case D_CmdKind_StepInto: case D_CmdKind_StepOver: