@@ -10,6 +10,7 @@ use ghostscope_dwarf::{ComputeStep, MemoryAccessSize};
1010use regex:: Regex ;
1111use std:: collections:: HashMap ;
1212use std:: fs;
13+ use std:: io:: Write ;
1314use std:: path:: { Path , PathBuf } ;
1415use std:: process:: Command as StdCommand ;
1516use std:: sync:: OnceLock ;
@@ -101,18 +102,15 @@ exec {binary} >>{stdout} 2>>{stderr}
101102 stdout = shell_quote( & stdout_log) ,
102103 stderr = shell_quote( & stderr_log) ,
103104 ) ;
104- fs :: write ( & wrapper_path, wrapper) ?;
105+ write_wrapper_script ( & wrapper_path, & wrapper) ?;
105106 #[ cfg( unix) ]
106107 {
107108 use std:: os:: unix:: fs:: PermissionsExt ;
108109 let mut perms = fs:: metadata ( & wrapper_path) ?. permissions ( ) ;
109110 perms. set_mode ( 0o755 ) ;
110111 fs:: set_permissions ( & wrapper_path, perms) ?;
111112 }
112- let target = TargetLauncher :: binary ( & wrapper_path)
113- . current_dir ( base)
114- . spawn ( )
115- . await ?;
113+ let target = spawn_wrapper_target ( & wrapper_path, base) . await ?;
116114 tokio:: time:: sleep ( Duration :: from_millis ( 500 ) ) . await ;
117115 Ok ( LoggedTarget {
118116 target,
@@ -129,6 +127,41 @@ fn create_runtime_log_dir(base: &Path) -> anyhow::Result<PathBuf> {
129127 Ok ( path)
130128}
131129
130+ fn write_wrapper_script ( path : & Path , contents : & str ) -> anyhow:: Result < ( ) > {
131+ let mut file = fs:: File :: create ( path) ?;
132+ file. write_all ( contents. as_bytes ( ) ) ?;
133+ file. sync_all ( ) ?;
134+ Ok ( ( ) )
135+ }
136+
137+ async fn spawn_wrapper_target ( wrapper_path : & Path , base : & Path ) -> anyhow:: Result < TargetHandle > {
138+ let mut delay = Duration :: from_millis ( 50 ) ;
139+ for attempt in 0 ..3 {
140+ match TargetLauncher :: binary ( wrapper_path)
141+ . current_dir ( base)
142+ . spawn ( )
143+ . await
144+ {
145+ Ok ( target) => return Ok ( target) ,
146+ Err ( err) if attempt < 2 && is_text_file_busy ( & err) => {
147+ tokio:: time:: sleep ( delay) . await ;
148+ delay *= 2 ;
149+ }
150+ Err ( err) => return Err ( err) ,
151+ }
152+ }
153+ unreachable ! ( "spawn retry loop should return on success or the final error" )
154+ }
155+
156+ fn is_text_file_busy ( err : & anyhow:: Error ) -> bool {
157+ err. chain ( ) . any ( |cause| {
158+ cause
159+ . downcast_ref :: < std:: io:: Error > ( )
160+ . and_then ( std:: io:: Error :: raw_os_error)
161+ == Some ( libc:: ETXTBSY )
162+ } )
163+ }
164+
132165fn read_log_file ( path : & Path ) -> anyhow:: Result < String > {
133166 match fs:: read_to_string ( path) {
134167 Ok ( contents) => Ok ( contents) ,
0 commit comments