99//! In the future, this crate will also provide a WIT-level API and
1010//! world in which to run debugger components.
1111
12- use std:: { any:: Any , sync:: Arc } ;
13- use tokio:: {
14- sync:: { Mutex , mpsc} ,
15- task:: JoinHandle ,
16- } ;
12+ use std:: { any:: Any , future:: Future , pin:: Pin , sync:: Arc } ;
13+ use tokio:: sync:: { Mutex , mpsc} ;
1714use wasmtime:: {
18- AsContextMut , DebugEvent , DebugHandler , ExnRef , OwnedRooted , Result , Store , StoreContextMut ,
19- Trap ,
15+ AsContextMut , DebugEvent , DebugHandler , Engine , ExnRef , OwnedRooted , Result , Store ,
16+ StoreContextMut , Trap ,
2017} ;
2118
2219/// A `Debugger` wraps up state associated with debugging the code
@@ -31,13 +28,15 @@ use wasmtime::{
3128/// state. One runs until the next event suspends execution by
3229/// invoking `Debugger::run`.
3330pub struct Debugger < T : Send + ' static > {
34- /// The inner task that this debugger wraps .
35- inner : Option < JoinHandle < Result < Store < T > > > > ,
31+ /// A handle to the Engine that the debuggee store lives within .
32+ engine : Engine ,
3633 /// State: either a task handle or the store when passed out of
3734 /// the complete task.
3835 state : DebuggerState ,
36+ /// The store, once complete.
37+ store : Option < Store < T > > ,
3938 in_tx : mpsc:: Sender < Command < T > > ,
40- out_rx : mpsc:: Receiver < Response > ,
39+ out_rx : mpsc:: Receiver < Response < T > > ,
4140}
4241
4342/// State machine from the perspective of the outer logic.
@@ -75,6 +74,8 @@ pub struct Debugger<T: Send + 'static> {
7574/// ```
7675#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
7776enum DebuggerState {
77+ /// Inner body has just been started.
78+ Initial ,
7879 /// Inner body is running in an async task and not in a debugger
7980 /// callback. Outer logic is waiting for a `Response::Paused` or
8081 /// `Response::Complete`.
@@ -128,15 +129,15 @@ enum Command<T: 'static> {
128129 Query ( Box < dyn FnOnce ( StoreContextMut < ' _ , T > ) -> Box < dyn Any + Send > + Send > ) ,
129130}
130131
131- enum Response {
132+ enum Response < T : ' static > {
132133 Paused ( DebugRunResult ) ,
133134 QueryResponse ( Box < dyn Any + Send > ) ,
134- Finished ,
135+ Finished ( Store < T > ) ,
135136}
136137
137138struct HandlerInner < T : Send + ' static > {
138139 in_rx : Mutex < mpsc:: Receiver < Command < T > > > ,
139- out_tx : mpsc:: Sender < Response > ,
140+ out_tx : mpsc:: Sender < Response < T > > ,
140141}
141142
142143struct Handler < T : Send + ' static > ( Arc < HandlerInner < T > > ) ;
@@ -201,40 +202,47 @@ impl<T: Send + 'static> Debugger<T> {
201202 ///
202203 /// When paused, the holder of this object can access the `Store`
203204 /// indirectly by providing a closure
204- pub fn new < F , I > ( mut store : Store < T > , inner : F ) -> Debugger < T >
205+ pub fn new < F > ( mut store : Store < T > , inner : F ) -> Debugger < T >
205206 where
206- I : Future < Output = Result < Store < T > > > + Send + ' static ,
207- F : for < ' a > FnOnce ( Store < T > ) -> I + Send + ' static ,
207+ F : for < ' a > FnOnce (
208+ & ' a mut Store < T > ,
209+ ) -> Pin < Box < dyn Future < Output = Result < ( ) > > + Send + ' a > >
210+ + Send
211+ + ' static ,
208212 {
209- let ( in_tx, mut in_rx) = mpsc:: channel ( 1 ) ;
213+ let engine = store. engine ( ) . clone ( ) ;
214+ let ( in_tx, in_rx) = mpsc:: channel ( 1 ) ;
210215 let ( out_tx, out_rx) = mpsc:: channel ( 1 ) ;
211216
212- let inner = tokio:: spawn ( async move {
213- // Receive one "continue" command on the inbound channel
214- // before continuing.
215- match in_rx. recv ( ) . await {
216- Some ( cmd) => {
217- assert ! ( matches!( cmd, Command :: Continue ) ) ;
218- }
219- None => {
220- // Premature exit due to closed channel. Just drop `inner`.
221- wasmtime:: bail!( "Debugger channel dropped" ) ;
222- }
223- }
224-
217+ tokio:: spawn ( async move {
218+ // Create the handler that's invoked from within the async
219+ // debug-event callback.
225220 let out_tx_clone = out_tx. clone ( ) ;
226- store . set_debug_handler ( Handler ( Arc :: new ( HandlerInner {
221+ let handler = Handler ( Arc :: new ( HandlerInner {
227222 in_rx : Mutex :: new ( in_rx) ,
228223 out_tx,
229- } ) ) ) ;
230- let result = inner ( store) . await ;
231- let _ = out_tx_clone. send ( Response :: Finished ) . await ;
224+ } ) ) ;
225+
226+ // Emulate a breakpoint at startup.
227+ log:: trace!( "inner debuggee task: first breakpoint" ) ;
228+ handler
229+ . handle ( store. as_context_mut ( ) , DebugEvent :: Breakpoint )
230+ . await ;
231+ log:: trace!( "inner debuggee task: first breakpoint resumed" ) ;
232+
233+ // Now invoke the actual inner body.
234+ store. set_debug_handler ( handler) ;
235+ log:: trace!( "inner debuggee task: running `inner`" ) ;
236+ let result = inner ( & mut store) . await ;
237+ log:: trace!( "inner debuggee task: done with `inner`" ) ;
238+ let _ = out_tx_clone. send ( Response :: Finished ( store) ) . await ;
232239 result
233240 } ) ;
234241
235242 Debugger {
236- inner : Some ( inner) ,
237- state : DebuggerState :: Paused ,
243+ engine,
244+ state : DebuggerState :: Initial ,
245+ store : None ,
238246 in_tx,
239247 out_rx,
240248 }
@@ -248,12 +256,35 @@ impl<T: Send + 'static> Debugger<T> {
248256 }
249257 }
250258
259+ /// Get the Engine associated with the debuggee.
260+ pub fn engine ( & self ) -> & Engine {
261+ & self . engine
262+ }
263+
264+ async fn wait_for_initial ( & mut self ) -> Result < ( ) > {
265+ if let DebuggerState :: Initial = & self . state {
266+ // Need to receive and discard first `Paused`.
267+ let response = self
268+ . out_rx
269+ . recv ( )
270+ . await
271+ . ok_or_else ( || wasmtime:: format_err!( "Premature close of debugger channel" ) ) ?;
272+ assert ! ( matches!( response, Response :: Paused ( _) ) ) ;
273+ self . state = DebuggerState :: Paused ;
274+ }
275+ Ok ( ( ) )
276+ }
277+
251278 /// Run the inner body until the next debug event.
252279 ///
253280 /// This method is cancel-safe, and no events will be lost.
254281 pub async fn run ( & mut self ) -> Result < DebugRunResult > {
255282 log:: trace!( "running: state is {:?}" , self . state) ;
283+
284+ self . wait_for_initial ( ) . await ?;
285+
256286 match self . state {
287+ DebuggerState :: Initial => unreachable ! ( ) ,
257288 DebuggerState :: Paused => {
258289 log:: trace!( "sending Continue" ) ;
259290 self . in_tx
@@ -311,9 +342,10 @@ impl<T: Send + 'static> Debugger<T> {
311342 . ok_or_else ( || wasmtime:: format_err!( "Premature close of debugger channel" ) ) ?;
312343
313344 match response {
314- Response :: Finished => {
345+ Response :: Finished ( store ) => {
315346 log:: trace!( "got Finished" ) ;
316347 self . state = DebuggerState :: Complete ;
348+ self . store = Some ( store) ;
317349 Ok ( DebugRunResult :: Finished )
318350 }
319351 Response :: Paused ( result) => {
@@ -362,9 +394,14 @@ impl<T: Send + 'static> Debugger<T> {
362394 & mut self ,
363395 f : F ,
364396 ) -> Result < R > {
365- assert ! ( !self . is_complete( ) ) ;
397+ if let Some ( store) = self . store . as_mut ( ) {
398+ return Ok ( f ( store. as_context_mut ( ) ) ) ;
399+ }
400+
401+ self . wait_for_initial ( ) . await ?;
366402
367403 match self . state {
404+ DebuggerState :: Initial => unreachable ! ( ) ,
368405 DebuggerState :: Queried => {
369406 // Earlier query canceled; drop its response first.
370407 let response =
@@ -387,6 +424,7 @@ impl<T: Send + 'static> Debugger<T> {
387424 }
388425 }
389426
427+ log:: trace!( "sending query in with_store" ) ;
390428 self . in_tx
391429 . send ( Command :: Query ( Box :: new ( |store| Box :: new ( f ( store) ) ) ) )
392430 . await
@@ -405,29 +443,6 @@ impl<T: Send + 'static> Debugger<T> {
405443
406444 Ok ( * resp. downcast :: < R > ( ) . expect ( "type mismatch" ) )
407445 }
408-
409- /// Drop the Debugger once complete, returning the inner `Store`
410- /// around which it was wrapped.
411- ///
412- /// Only valid to invoke once `run()` returns
413- /// `DebugRunResult::Finished` or after calling `finish()` (which
414- /// finishes execution while dropping all further debug events).
415- ///
416- /// This is cancel-safe, but if canceled, the Store is lost.
417- pub async fn take_store ( & mut self ) -> Result < Option < Store < T > > > {
418- match self . state {
419- DebuggerState :: Complete => {
420- let inner = match self . inner . take ( ) {
421- Some ( inner) => inner,
422- None => return Ok ( None ) ,
423- } ;
424- let mut store = inner. await ??;
425- store. clear_debug_handler ( ) ;
426- Ok ( Some ( store) )
427- }
428- _ => panic ! ( "Invalid state: debugger not yet complete" ) ,
429- }
430- }
431446}
432447
433448/// The result of one call to `Debugger::run()`.
@@ -482,16 +497,18 @@ mod test {
482497 let instance = Instance :: new_async ( & mut store, & module, & [ ] ) . await ?;
483498 let main = instance. get_func ( & mut store, "main" ) . unwrap ( ) ;
484499
485- let mut debugger = Debugger :: new ( store, move |mut store| async move {
486- let mut results = [ Val :: I32 ( 0 ) ] ;
487- store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
488- main. call_async ( & mut store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
489- . await ?;
490- assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
491- main. call_async ( & mut store, & [ Val :: I32 ( 3 ) , Val :: I32 ( 4 ) ] , & mut results[ ..] )
492- . await ?;
493- assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 7 ) ;
494- Ok ( store)
500+ let mut debugger = Debugger :: new ( store, move |store| {
501+ Box :: pin ( async move {
502+ let mut results = [ Val :: I32 ( 0 ) ] ;
503+ store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
504+ main. call_async ( & mut * store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
505+ . await ?;
506+ assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
507+ main. call_async ( & mut * store, & [ Val :: I32 ( 3 ) , Val :: I32 ( 4 ) ] , & mut results[ ..] )
508+ . await ?;
509+ assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 7 ) ;
510+ Ok ( ( ) )
511+ } )
495512 } ) ;
496513
497514 let event = debugger. run ( ) . await ?;
@@ -642,14 +659,6 @@ mod test {
642659
643660 assert ! ( debugger. is_complete( ) ) ;
644661
645- // Ensure the store still works and the debug handler is
646- // removed.
647- let mut store = debugger. take_store ( ) . await ?. unwrap ( ) ;
648- let mut results = [ Val :: I32 ( 0 ) ] ;
649- main. call_async ( & mut store, & [ Val :: I32 ( 10 ) , Val :: I32 ( 20 ) ] , & mut results[ ..] )
650- . await ?;
651- assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 30 ) ;
652-
653662 Ok ( ( ) )
654663 }
655664
@@ -676,13 +685,15 @@ mod test {
676685 let instance = Instance :: new_async ( & mut store, & module, & [ ] ) . await ?;
677686 let main = instance. get_func ( & mut store, "main" ) . unwrap ( ) ;
678687
679- let mut debugger = Debugger :: new ( store, move |mut store| async move {
680- let mut results = [ Val :: I32 ( 0 ) ] ;
681- store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
682- main. call_async ( & mut store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
683- . await ?;
684- assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
685- Ok ( store)
688+ let mut debugger = Debugger :: new ( store, move |store| {
689+ Box :: pin ( async move {
690+ let mut results = [ Val :: I32 ( 0 ) ] ;
691+ store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
692+ main. call_async ( & mut * store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
693+ . await ?;
694+ assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
695+ Ok ( ( ) )
696+ } )
686697 } ) ;
687698
688699 debugger. finish ( ) . await ?;
@@ -714,13 +725,15 @@ mod test {
714725 let instance = Instance :: new_async ( & mut store, & module, & [ ] ) . await ?;
715726 let main = instance. get_func ( & mut store, "main" ) . unwrap ( ) ;
716727
717- let mut debugger = Debugger :: new ( store, move |mut store| async move {
718- let mut results = [ Val :: I32 ( 0 ) ] ;
719- store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
720- main. call_async ( & mut store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
721- . await ?;
722- assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
723- Ok ( store)
728+ let mut debugger = Debugger :: new ( store, move |store| {
729+ Box :: pin ( async move {
730+ let mut results = [ Val :: I32 ( 0 ) ] ;
731+ store. edit_breakpoints ( ) . unwrap ( ) . single_step ( true ) . unwrap ( ) ;
732+ main. call_async ( & mut * store, & [ Val :: I32 ( 1 ) , Val :: I32 ( 2 ) ] , & mut results[ ..] )
733+ . await ?;
734+ assert_eq ! ( results[ 0 ] . unwrap_i32( ) , 3 ) ;
735+ Ok ( ( ) )
736+ } )
724737 } ) ;
725738
726739 // Step once, then drop everything at the end of this
0 commit comments