1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- use std:: process:: { Command , ExitStatus , Stdio } ;
4
+ use std:: process:: { Command , Stdio } ;
5
5
use std:: sync:: Arc ;
6
6
use std:: time:: Duration ;
7
7
@@ -120,32 +120,58 @@ impl CoverageRecorder {
120
120
121
121
#[ cfg( target_os = "windows" ) ]
122
122
pub fn record ( self ) -> Result < Recorded > {
123
+ use anyhow:: bail;
123
124
use debugger:: Debugger ;
125
+ use process_control:: { ChildExt , Control } ;
124
126
use windows:: WindowsRecorder ;
125
127
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
+
126
140
let loader = self . loader . clone ( ) ;
141
+ let mut recorder =
142
+ WindowsRecorder :: new ( & loader, self . module_allowlist , self . cache . as_ref ( ) ) ;
127
143
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)
143
155
}
156
+ Ok ( Ok ( None ) ) => {
157
+ bail ! ( crate :: timer:: TimerError :: Timeout ( self . timeout) )
158
+ }
159
+ Ok ( Ok ( Some ( output) ) ) => output,
160
+ } ;
144
161
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
+ }
146
169
147
- Ok ( Recorded { coverage, output } )
148
- } ) ?
170
+ let coverage = recorder. coverage ;
171
+ Ok ( Recorded {
172
+ coverage,
173
+ output : output. into ( ) ,
174
+ } )
149
175
}
150
176
}
151
177
@@ -157,19 +183,32 @@ pub struct Recorded {
157
183
158
184
#[ derive( Clone , Debug , Default ) ]
159
185
pub struct Output {
160
- pub status : Option < ExitStatus > ,
186
+ pub status : Option < process_control :: ExitStatus > ,
161
187
pub stderr : String ,
162
188
pub stdout : String ,
163
189
}
164
190
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
+
165
204
impl From < std:: process:: Output > for Output {
166
205
fn from ( output : std:: process:: Output ) -> Self {
167
206
let status = Some ( output. status ) ;
168
207
let stdout = String :: from_utf8_lossy ( & output. stdout ) . into_owned ( ) ;
169
208
let stderr = String :: from_utf8_lossy ( & output. stderr ) . into_owned ( ) ;
170
209
171
210
Self {
172
- status,
211
+ status : status . map ( Into :: into ) ,
173
212
stdout,
174
213
stderr,
175
214
}
0 commit comments