11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- use std:: process:: { Command , ExitStatus , Stdio } ;
4+ use std:: process:: { Command , Stdio } ;
55use std:: sync:: Arc ;
66use std:: time:: Duration ;
77
@@ -120,32 +120,58 @@ impl CoverageRecorder {
120120
121121 #[ cfg( target_os = "windows" ) ]
122122 pub fn record ( self ) -> Result < Recorded > {
123+ use anyhow:: bail;
123124 use debugger:: Debugger ;
125+ use process_control:: { ChildExt , Control } ;
124126 use windows:: WindowsRecorder ;
125127
128+ let child = Debugger :: create_child ( self . cmd ) ?;
129+
130+ // Spawn a thread to wait for the target process to exit.
131+ let taget_process = std:: thread:: spawn ( move || {
132+ let output = child
133+ . controlled_with_output ( )
134+ . time_limit ( self . timeout )
135+ . terminate_for_timeout ( )
136+ . wait ( ) ;
137+ output
138+ } ) ;
139+
126140 let loader = self . loader . clone ( ) ;
141+ let mut recorder =
142+ WindowsRecorder :: new ( & loader, self . module_allowlist , self . cache . as_ref ( ) ) ;
127143
128- crate :: timer:: timed ( self . timeout , move || {
129- let mut recorder =
130- WindowsRecorder :: new ( & loader, self . module_allowlist , self . cache . as_ref ( ) ) ;
131- let ( mut dbg, child) = Debugger :: init ( self . cmd , & mut recorder) ?;
132- dbg. run ( & mut recorder) ?;
133-
134- // If the debugger callbacks fail, this may return with a spurious clean exit.
135- let output = child. wait_with_output ( ) ?. into ( ) ;
136-
137- // Check if debugging was stopped due to a callback error.
138- //
139- // If so, the debugger terminated the target, and the recorded coverage and
140- // output are both invalid.
141- if let Some ( err) = recorder. stop_error {
142- return Err ( err) ;
144+ // The debugger is initialized in the same thread that created the target process to be able to receive the debug events
145+ let mut dbg = Debugger :: init_debugger ( & mut recorder) ?;
146+ dbg. run ( & mut recorder) ?;
147+
148+ // If the debugger callbacks fail, this may return with a spurious clean exit.
149+ let output = match taget_process. join ( ) {
150+ Err ( err) => {
151+ bail ! ( "failed to launch target thread: {:?}" , err)
152+ }
153+ Ok ( Err ( err) ) => {
154+ bail ! ( "failed to launch target process: {:?}" , err)
143155 }
156+ Ok ( Ok ( None ) ) => {
157+ bail ! ( crate :: timer:: TimerError :: Timeout ( self . timeout) )
158+ }
159+ Ok ( Ok ( Some ( output) ) ) => output,
160+ } ;
144161
145- let coverage = recorder. coverage ;
162+ // Check if debugging was stopped due to a callback error.
163+ //
164+ // If so, the debugger terminated the target, and the recorded coverage and
165+ // output are both invalid.
166+ if let Some ( err) = recorder. stop_error {
167+ return Err ( err) ;
168+ }
146169
147- Ok ( Recorded { coverage, output } )
148- } ) ?
170+ let coverage = recorder. coverage ;
171+ Ok ( Recorded {
172+ coverage,
173+ output : output. into ( ) ,
174+ } )
149175 }
150176}
151177
@@ -157,19 +183,32 @@ pub struct Recorded {
157183
158184#[ derive( Clone , Debug , Default ) ]
159185pub struct Output {
160- pub status : Option < ExitStatus > ,
186+ pub status : Option < process_control :: ExitStatus > ,
161187 pub stderr : String ,
162188 pub stdout : String ,
163189}
164190
191+ impl From < process_control:: Output > for Output {
192+ fn from ( output : process_control:: Output ) -> Self {
193+ let status = Some ( output. status ) ;
194+ let stdout = String :: from_utf8_lossy ( & output. stdout ) . into_owned ( ) ;
195+ let stderr = String :: from_utf8_lossy ( & output. stderr ) . into_owned ( ) ;
196+ Self {
197+ status,
198+ stdout,
199+ stderr,
200+ }
201+ }
202+ }
203+
165204impl From < std:: process:: Output > for Output {
166205 fn from ( output : std:: process:: Output ) -> Self {
167206 let status = Some ( output. status ) ;
168207 let stdout = String :: from_utf8_lossy ( & output. stdout ) . into_owned ( ) ;
169208 let stderr = String :: from_utf8_lossy ( & output. stderr ) . into_owned ( ) ;
170209
171210 Self {
172- status,
211+ status : status . map ( Into :: into ) ,
173212 stdout,
174213 stderr,
175214 }
0 commit comments