@@ -1438,25 +1438,10 @@ dmn_lnx_handle_create_thread(Arena *arena, DMN_EventList *events, DMN_LNX_Entity
14381438}
14391439
14401440internal void
1441- dmn_lnx_handle_create_process (Arena * arena , DMN_EventList * events , pid_t pid )
1441+ dmn_lnx_handle_create_process (Arena * arena , DMN_EventList * events , B32 debug_subprocesses , pid_t pid )
14421442{
14431443 Temp scratch = scratch_begin (& arena , 1 );
14441444
1445- //
1446- // move pid to the active list
1447- //
1448- B32 debug_subprocesses = 0 ;
1449- for EachNode (n , DMN_LNX_ProcessLaunch , dmn_lnx_state -> pending_creation .first )
1450- {
1451- if (n -> pid == pid )
1452- {
1453- dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> pending_creation , n );
1454- dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> free_pids , n );
1455- debug_subprocesses = n -> debug_subprocesses ;
1456- break ;
1457- }
1458- }
1459-
14601445 ELF_Hdr64 exe_ehdr = dmn_lnx_ehdr_from_pid (pid );
14611446 int memory_fd = open ((char * )str8f (scratch .arena , "/proc/%d/mem" , pid ).str , O_RDWR );
14621447 DMN_LNX_ProcessAuxv auxv = dmn_lnx_auxv_from_pid (pid , exe_ehdr .e_ident [ELF_Identifier_Class ]);
@@ -1559,7 +1544,6 @@ dmn_lnx_handle_create_process(Arena *arena, DMN_EventList *events, pid_t pid)
15591544 process -> xcr0 = xcr0 ;
15601545 process -> xsave_size = Max (xsave_size , sizeof (X64_XSave ));
15611546 process -> xsave_layout = xsave_layout ;
1562- process -> thread_count = 1 ;
15631547 MemoryCopyTyped (& process -> probe_vaddrs [0 ], & probe_vaddrs [0 ], DMN_LNX_ProbeType_Count );
15641548 hash_table_push_u64_raw (dmn_lnx_state -> arena , dmn_lnx_state -> pid_ht , pid , process );
15651549 // push create process event
@@ -2091,95 +2075,89 @@ dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params)
20912075{
20922076 Temp scratch = scratch_begin (0 , 0 );
20932077
2094- pid_t pid = 0 ;
2095-
2096- //- rjf: unpack command line
2097- int argc = (int )(params -> cmd_line .node_count );
2098- char * * argv = push_array (scratch .arena , char * , argc + 1 );
2078+ // setup target command line
2079+ U64 argc = params -> cmd_line .node_count + 1 ;
2080+ char * * argv = push_array (scratch .arena , char * , argc );
20992081 {
21002082 U64 idx = 0 ;
21012083 for (String8Node * n = params -> cmd_line .first ; n != 0 ; n = n -> next , idx += 1 )
21022084 {
21032085 argv [idx ] = (char * )push_str8_copy (scratch .arena , n -> string ).str ;
21042086 }
21052087 }
2106-
2107- //- rjf: unpack path
2108- char * path = (char * )push_str8_copy (scratch .arena , params -> path ).str ;
2109-
2110- //- rjf: unpack environment
2111- char * * env = push_array (scratch .arena , char * , os_lnx_state .default_env_count + params -> env .node_count + 1 );
2088+
2089+ // setup target environment
2090+ U64 envc = os_lnx_state .default_env_count + params -> env .node_count + 1 ;
2091+ char * * envp = push_array (scratch .arena , char * , envc );
21122092 {
2113- MemoryCopyTyped (env , os_lnx_state .default_env , os_lnx_state .default_env_count );
2093+ // copy default environment
2094+ MemoryCopyTyped (envp , os_lnx_state .default_env , os_lnx_state .default_env_count );
21142095
2096+ // copy user environment
21152097 U64 idx = os_lnx_state .default_env_count ;
21162098 for (String8Node * n = params -> env .first ; n != 0 ; n = n -> next , idx += 1 )
21172099 {
2118- env [idx ] = (char * )push_str8_copy (scratch .arena , n -> string ).str ;
2100+ envp [idx ] = (char * )push_str8_copy (scratch .arena , n -> string ).str ;
21192101 }
21202102 }
21212103
2122- //- rjf: fork
2123- pid = fork ();
2104+ // create zero-terminated work directory path
2105+ char * work_dir_path = (char * )push_str8_copy (scratch .arena , params -> path ).str ;
2106+
2107+ // fork process
2108+ pid_t pid = fork ();
21242109
21252110 // child process
21262111 if (pid == 0 )
21272112 {
2128- // set current working directory to tracee
2129- if (OS_LNX_RETRY_ON_EINTR (chdir (path )) < 0 ) { goto child_exit ; }
2113+ // change work directory to tracee
2114+ if (OS_LNX_RETRY_ON_EINTR (chdir (work_dir_path )) < 0 ) { goto child_exit ; }
21302115
2131- // notify parent that we are going to execve
2132- OS_LNX_RETRY_ON_EINTR (raise (SIGSTOP ));
2116+ // @first_sigstop notify parent that we are going to execve
2117+ if ( OS_LNX_RETRY_ON_EINTR (raise (SIGSTOP )) < 0 ) { goto child_exit ; }
21332118
2134- // replace process with target
2135- OS_LNX_RETRY_ON_EINTR (execve (argv [0 ], argv , env ));
2119+ // replace process with target program
2120+ if ( OS_LNX_RETRY_ON_EINTR (execve (argv [0 ], argv , envp )) < 0 ) { goto child_exit ; }
21362121
2137- // execve failed -- exit
21382122 child_exit :;
21392123 abort ();
21402124 }
21412125 // parent process
21422126 else if (pid > 0 )
21432127 {
2144- B32 kill_child_process = 1 ;
2128+ B32 failed_to_seize = 1 ;
21452129
21462130 // try to seize child process
2147- if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_SEIZE , pid , 0 , 0 ) < 0 )) { Assert ( 0 && "failed to seize" ); goto parent_exit ; }
2131+ if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_SEIZE , pid , 0 , 0 ) < 0 )) { goto parent_exit ; }
21482132
2149- if (dmn_lnx_state -> tracer_tid == 0 )
2150- {
2151- dmn_lnx_state -> tracer_tid = gettid ();
2152- }
2153- else
2154- {
2155- AssertAlways (dmn_lnx_state -> tracer_tid == gettid ());
2156- }
2133+ // ensure tracer ops are issued on thread that sizes processes
2134+ if (dmn_lnx_state -> tracer_tid == 0 ) { dmn_lnx_state -> tracer_tid = gettid (); }
2135+ AssertAlways (dmn_lnx_state -> tracer_tid == gettid ());
21572136
2158- // alloc pid node
2159- DMN_LNX_ProcessLaunch * pending_stopsig = dmn_lnx_state -> free_pids .first ;
2160- if (pending_stopsig ) { dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> free_pids , pending_stopsig ); }
2161- else { pending_stopsig = push_array (dmn_lnx_state -> arena , DMN_LNX_ProcessLaunch , 1 ); }
2137+ // alloc pending process node
2138+ DMN_LNX_ProcessLaunch * pending_proc = dmn_lnx_state -> free_pids .first ;
2139+ if (pending_proc ) { dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> free_pids , pending_proc ); }
2140+ else { pending_proc = push_array (dmn_lnx_state -> arena , DMN_LNX_ProcessLaunch , 1 ); }
21622141
2163- // add to list
2164- pending_stopsig -> debug_subprocesses = params -> debug_subprocesses ;
2165- pending_stopsig -> pid = pid ;
2166- dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> pending_stopsig , pending_stopsig );
2142+ // add pending proc node to the list
2143+ * pending_proc = (DMN_LNX_ProcessLaunch ){ params -> debug_subprocesses , pid , DMN_LNX_ProcessLaunchState_SigStop };
2144+ dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> pending_procs , pending_proc );
21672145
2168- // tracee was successfully sizeed
2169- kill_child_process = 0 ;
2146+ // tracee was successfully seized
2147+ failed_to_seize = 0 ;
21702148
21712149 parent_exit :;
2172- if (kill_child_process )
2150+ if (failed_to_seize )
21732151 {
21742152 Assert (0 && "failed to ptrace child process" );
2175- if (OS_LNX_RETRY_ON_EINTR (kill (SIGKILL , pid )) < 0 ) { Assert (0 && "failed to kill child process" ); }
2153+ OS_LNX_RETRY_ON_EINTR (kill (SIGKILL , pid ));
2154+ OS_LNX_RETRY_ON_EINTR (waitpid (pid , 0 , WNOHANG ));
21762155 pid = 0 ;
21772156 }
21782157 }
21792158
21802159 scratch_end (scratch );
2181- Assert (pid < max_U32 );
2182- return (U32 )pid ;
2160+ return pid ;
21832161}
21842162
21852163internal B32
@@ -2394,64 +2372,97 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
23942372
23952373 // wait for signals from the running threads
23962374 {
2397- U64 stopped_threads = 0 ;
2398- B32 is_halt_done = 0 ;
2399- do
2375+ B32 is_halt_done = 0 ;
2376+ for (U64 stopped_threads = 0 ;;)
24002377 {
2401- // do not wait if there are no processes
2402- if (dmn_lnx_state -> pending_stopsig . count == 0 && dmn_lnx_state -> pending_creation .count == 0 && dmn_lnx_state -> active_process_count == 0 )
2378+ // do not wait if there are no pending or active processes
2379+ if (dmn_lnx_state -> pending_procs .count == 0 && dmn_lnx_state -> active_process_count == 0 )
24032380 {
24042381 dmn_lnx_handle_not_attached (arena , & events );
24052382 break ;
24062383 }
24072384
2408- mutex_drop (dmn_lnx_state -> halter_mutex );
2409-
24102385 // wait for a signal
24112386 int status = 0 ;
24122387 pid_t wait_id = 0 ;
2413- for (;;)
24142388 {
2415- wait_id = OS_LNX_RETRY_ON_EINTR (waitpid (-1 , & status , __WALL |__WNOTHREAD ));
2416- if (wait_id == -1 ) { InvalidPath ; } // TODO: graceful exit
2417- break ;
2389+ mutex_drop (dmn_lnx_state -> halter_mutex );
2390+ for (;;)
2391+ {
2392+ wait_id = OS_LNX_RETRY_ON_EINTR (waitpid (-1 , & status , __WALL |__WNOTHREAD ));
2393+ if (wait_id == -1 ) { InvalidPath ; } // TODO: graceful exit
2394+ break ;
2395+ }
2396+ mutex_take (dmn_lnx_state -> halter_mutex );
24182397 }
24192398
2420- mutex_take (dmn_lnx_state -> halter_mutex );
2421-
24222399 // unpack status
24232400 int wifexited = WIFEXITED (status );
24242401 int wifsignaled = WIFSIGNALED (status );
24252402 int wifstopped = WIFSTOPPED (status );
24262403 int wstopsig = WSTOPSIG (status );
24272404 int event_code = (status >> 16 );
24282405
2429- // intercept our SIGSTOP and signal tracee to continue to execve
2430- if (wstopsig == SIGSTOP )
2406+ // intercept signals meant for the process launch sequence
24312407 {
2432- B32 is_pending_launch = 0 ;
2433- for EachNode (n , DMN_LNX_ProcessLaunch , dmn_lnx_state -> pending_stopsig .first )
2408+ // pid -> pending process launch
2409+ DMN_LNX_ProcessLaunch * pending_proc = 0 ;
2410+ for EachNode (n , DMN_LNX_ProcessLaunch , dmn_lnx_state -> pending_procs .first ) { if (n -> pid == wait_id ) { pending_proc = n ; break ; } }
2411+
2412+ if (pending_proc )
24342413 {
2435- if (n -> pid == wait_id )
2414+ // process unexpectedly exited
2415+ if (wifexited ) { pending_proc -> state = DMN_LNX_ProcessLaunchState_Exit ; }
2416+
2417+ if (pending_proc -> state == DMN_LNX_ProcessLaunchState_Exec && wstopsig == SIGTRAP && event_code == PTRACE_EVENT_EXEC )
24362418 {
2437- // move node to the pending creation list
2438- dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> pending_stopsig , n );
2439- dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> pending_creation , n );
2419+ // move pending process node to the free list
2420+ dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> pending_procs , pending_proc );
2421+ dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> free_pids , pending_proc );
24402422
2441- // set trace options
2442- // TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFROK | PTRACE_O_TRACEVFORKDONE
2443- void * trace_options = (void * )(PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE );
2444- if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_SETOPTIONS , wait_id , 0 , trace_options )) < 0 ) { Assert (0 && "failed to set options" ); InvalidPath ; }
2423+ // push create process events
2424+ dmn_lnx_handle_create_process (arena , & events , pending_proc -> debug_subprocesses , wait_id );
24452425
2446- // signal process to continue to execve
2447- if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_CONT , wait_id , 0 , 0 )) < 0 ) { InvalidPath ; }
2426+ // override event code so the main code path does not create another process
2427+ event_code = PTRACE_EVENT_STOP ;
2428+ }
2429+ else
2430+ {
2431+ B32 shutdown_process = 1 ;
24482432
2449- is_pending_launch = 1 ;
2433+ if (pending_proc -> state == DMN_LNX_ProcessLaunchState_SigStop && wstopsig == SIGSTOP ) // @first_sigstop
2434+ {
2435+ // set trace options and singal to proceed to the execve
2436+ //
2437+ // TODO: PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFROK | PTRACE_O_TRACEVFORKDONE
2438+ if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_SETOPTIONS , wait_id , 0 , (void * )(PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE ))) >= 0 )
2439+ {
2440+ if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_CONT , wait_id , 0 , 0 )) >= 0 )
2441+ {
2442+ // update pending process state
2443+ pending_proc -> state = DMN_LNX_ProcessLaunchState_Exec ;
2444+ shutdown_process = 0 ;
2445+ }
2446+ }
2447+ }
2448+ else if (pending_proc -> state == DMN_LNX_ProcessLaunchState_Exit )
2449+ {
2450+ // move pending process node to the free list
2451+ dmn_lnx_process_launch_list_remove (& dmn_lnx_state -> pending_procs , pending_proc );
2452+ dmn_lnx_process_launch_list_push_node (& dmn_lnx_state -> free_pids , pending_proc );
2453+ shutdown_process = 0 ;
2454+ }
24502455
2451- break ;
2456+ // exit on abnormal process init
2457+ if (shutdown_process )
2458+ {
2459+ OS_LNX_RETRY_ON_EINTR (kill (wait_id , SIGKILL ));
2460+ pending_proc -> state = DMN_LNX_ProcessLaunchState_Exit ;
2461+ }
2462+
2463+ continue ;
24522464 }
24532465 }
2454- if (is_pending_launch ) { continue ; }
24552466 }
24562467
24572468 if (wifstopped || wifsignaled || wifexited )
@@ -2561,26 +2572,18 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
25612572 {
25622573 DMN_LNX_Entity * thread = dmn_lnx_thread_from_pid (wait_id );
25632574 DMN_LNX_Entity * process = thread -> parent ;
2564-
2565- if (process == dmn_lnx_nil_entity )
2575+ if (process -> debug_subprocesses )
25662576 {
2567- dmn_lnx_handle_create_process (arena , & events , wait_id );
2577+ dmn_lnx_handle_exit_thread (arena , & events , wait_id , 0 );
2578+ dmn_lnx_handle_create_process (arena , & events , 1 , wait_id );
25682579 }
25692580 else
25702581 {
2571- if (process -> debug_subprocesses )
2582+ if (OS_LNX_RETRY_ON_EINTR ( ptrace ( PTRACE_DETACH , wait_id , 0 , 0 )) >= 0 )
25722583 {
25732584 dmn_lnx_handle_exit_thread (arena , & events , wait_id , 0 );
2574- dmn_lnx_handle_create_process (arena , & events , wait_id );
2575- }
2576- else
2577- {
2578- if (OS_LNX_RETRY_ON_EINTR (ptrace (PTRACE_DETACH , wait_id , 0 , 0 )) >= 0 )
2579- {
2580- dmn_lnx_handle_exit_thread (arena , & events , wait_id , 0 );
2581- }
2582- else { Assert (0 && "failed to detach" ); }
25832585 }
2586+ else { Assert (0 && "failed to detach" ); }
25842587 }
25852588 }break ;
25862589 case PTRACE_EVENT_VFORK_DONE :
@@ -2630,7 +2633,13 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
26302633 }
26312634 }
26322635 else { Assert (0 && "unexpected stop code" ); }
2633- } while (stopped_threads < running_threads .count );
2636+
2637+ // do not wait if all threads are stopped and there are no launching processes
2638+ if (stopped_threads >= running_threads .count && dmn_lnx_state -> pending_procs .count == 0 )
2639+ {
2640+ break ;
2641+ }
2642+ }
26342643
26352644 // finalize halter state
26362645 if (is_halt_done )
0 commit comments