xref: /wasmtime-44.0.1/crates/debugger/src/lib.rs (revision 58877f2f)
1 //! Wasmtime debugger functionality.
2 //!
3 //! This crate builds on top of the core Wasmtime crate's
4 //! guest-debugger APIs to present an environment where a debugger
5 //! runs as a "co-running process" and sees the debugee as a a
6 //! provider of a stream of events, on which actions can be taken
7 //! between each event.
8 //!
9 //! In the future, this crate will also provide a WIT-level API and
10 //! world in which to run debugger components.
11 
12 use std::{any::Any, future::Future, pin::Pin, sync::Arc};
13 use tokio::sync::{Mutex, mpsc};
14 use wasmtime::{
15     AsContextMut, DebugEvent, DebugHandler, Engine, ExnRef, OwnedRooted, Result, Store,
16     StoreContextMut, Trap,
17 };
18 
19 /// A `Debugger` wraps up state associated with debugging the code
20 /// running in a single `Store`.
21 ///
22 /// It acts as a Future combinator, wrapping an inner async body that
23 /// performs some actions on a store. Those actions are subject to the
24 /// debugger, and debugger events will be raised as appropriate. From
25 /// the "outside" of this combinator, it is always in one of two
26 /// states: running or paused. When paused, it acts as a
27 /// `StoreContextMut` and can allow examining the paused execution's
28 /// state. One runs until the next event suspends execution by
29 /// invoking `Debugger::run`.
30 pub struct Debugger<T: Send + 'static> {
31     /// A handle to the Engine that the debuggee store lives within.
32     engine: Engine,
33     /// State: either a task handle or the store when passed out of
34     /// the complete task.
35     state: DebuggerState,
36     /// The store, once complete.
37     store: Option<Store<T>>,
38     in_tx: mpsc::Sender<Command<T>>,
39     out_rx: mpsc::Receiver<Response<T>>,
40 }
41 
42 /// State machine from the perspective of the outer logic.
43 ///
44 /// The intermediate states here, and the separation of these states
45 /// from the `JoinHandle` above, are what allow us to implement a
46 /// cancel-safe version of `Debugger::run` below.
47 ///
48 /// The state diagram for the outer logic is:
49 ///
50 /// ```plain
51 ///              (start)
52 ///                 v
53 ///                 |
54 /// .--->---------. v
55 /// |     .----<  Paused  <-----------------------------------------------.
56 /// |     |         v                                                     |
57 /// |     |         | (async fn run() starts, sends Command::Continue)    |
58 /// |     |         |                                                     |
59 /// |     |         v                                                     ^
60 /// |     |      Running                                                  |
61 /// |     |       v v (async fn run() receives Response::Paused, returns) |
62 /// |     |       | |_____________________________________________________|
63 /// |     |       |
64 /// |     |       | (async fn run() receives Response::Finished, returns)
65 /// |     |       v
66 /// |     |     Complete
67 /// |     |
68 /// ^     | (async fn with_store() starts, sends Command::Query)
69 /// |     v
70 /// |   Queried
71 /// |     |
72 /// |     | (async fn with_store() receives Response::QueryResponse, returns)
73 /// `---<-'
74 /// ```
75 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
76 enum DebuggerState {
77     /// Inner body has just been started.
78     Initial,
79     /// Inner body is running in an async task and not in a debugger
80     /// callback. Outer logic is waiting for a `Response::Paused` or
81     /// `Response::Complete`.
82     Running,
83     /// Inner body is running in an async task and at a debugger
84     /// callback (or in the initial trampoline waiting for the first
85     /// `Continue`). `Response::Paused` has been received. Outer
86     /// logic has not sent any commands.
87     Paused,
88     /// We have sent a command to the inner body and are waiting for a
89     /// response.
90     Queried,
91     /// Inner body is complete (has sent `Response::Finished` and we
92     /// have received it). We may or may not have joined yet; if so,
93     /// the `Option<JoinHandle<...>>` will be `None`.
94     Complete,
95 }
96 
97 /// Message from "outside" to the debug hook.
98 ///
99 /// The `Query` catch-all with a boxed closure is a little janky, but
100 /// is the way that we provide access
101 /// from outside to the Store (which is owned by `inner` above)
102 /// only during pauses. Note that the future cannot take full
103 /// ownership or a mutable borrow of the Store, because it cannot
104 /// hold this across async yield points.
105 ///
106 /// Instead, the debugger body sends boxed closures which take the
107 /// Store as a parameter (lifetime-limited not to escape that
108 /// closure) out to this crate's implementation that runs inside of
109 /// debugger-instrumentation callbacks (which have access to the
110 /// Store during their duration). We send return values
111 /// back. Return values are boxed Any values.
112 ///
113 /// If we wanted to make this a little more principled, we could
114 /// come up with a Command/Response pair of enums for all possible
115 /// closures and make everything more statically typed and less
116 /// Box'd, but that would severely restrict the flexibility of the
117 /// abstraction here and essentially require writing a full proxy
118 /// of the debugger API.
119 ///
120 /// Furthermore, we expect to rip this out eventually when we move
121 /// the debugger over to an async implementation based on
122 /// `run_concurrent` and `Accessor`s (see #11896). Building things
123 /// this way now will actually allow a less painful transition at
124 /// that time, because we will have a bunch of closures accessing
125 /// the store already and we can run those "with an accessor"
126 /// instead.
127 enum Command<T: 'static> {
128     Continue,
129     Query(Box<dyn FnOnce(StoreContextMut<'_, T>) -> Box<dyn Any + Send> + Send>),
130 }
131 
132 enum Response<T: 'static> {
133     Paused(DebugRunResult),
134     QueryResponse(Box<dyn Any + Send>),
135     Finished(Store<T>),
136 }
137 
138 struct HandlerInner<T: Send + 'static> {
139     in_rx: Mutex<mpsc::Receiver<Command<T>>>,
140     out_tx: mpsc::Sender<Response<T>>,
141 }
142 
143 struct Handler<T: Send + 'static>(Arc<HandlerInner<T>>);
144 
145 impl<T: Send + 'static> std::clone::Clone for Handler<T> {
146     fn clone(&self) -> Self {
147         Handler(self.0.clone())
148     }
149 }
150 
151 impl<T: Send + 'static> DebugHandler for Handler<T> {
152     type Data = T;
153     async fn handle(&self, mut store: StoreContextMut<'_, T>, event: DebugEvent<'_>) {
154         let mut in_rx = self.0.in_rx.lock().await;
155 
156         let result = match event {
157             DebugEvent::HostcallError(_) => DebugRunResult::HostcallError,
158             DebugEvent::CaughtExceptionThrown(exn) => DebugRunResult::CaughtExceptionThrown(exn),
159             DebugEvent::UncaughtExceptionThrown(exn) => {
160                 DebugRunResult::UncaughtExceptionThrown(exn)
161             }
162             DebugEvent::Trap(trap) => DebugRunResult::Trap(trap),
163             DebugEvent::Breakpoint => DebugRunResult::Breakpoint,
164             DebugEvent::EpochYield => DebugRunResult::EpochYield,
165         };
166         self.0
167             .out_tx
168             .send(Response::Paused(result))
169             .await
170             .expect("outbound channel closed prematurely");
171 
172         while let Some(cmd) = in_rx.recv().await {
173             match cmd {
174                 Command::Query(closure) => {
175                     let result = closure(store.as_context_mut());
176                     self.0
177                         .out_tx
178                         .send(Response::QueryResponse(result))
179                         .await
180                         .expect("outbound channel closed prematurely");
181                 }
182                 Command::Continue => {
183                     break;
184                 }
185             }
186         }
187     }
188 }
189 
190 impl<T: Send + 'static> Debugger<T> {
191     /// Create a new Debugger that attaches to the given Store and
192     /// runs the given inner body.
193     ///
194     /// The debugger is always in one of two states: running or
195     /// paused.
196     ///
197     /// When paused, the holder of this object can invoke
198     /// `Debugger::run` to enter the running state. The inner body
199     /// will run until paused by a debug event. While running, the
200     /// future returned by either of these methods owns the `Debugger`
201     /// and hence no other methods can be invoked.
202     ///
203     /// When paused, the holder of this object can access the `Store`
204     /// indirectly by providing a closure
205     pub fn new<F>(mut store: Store<T>, inner: F) -> Debugger<T>
206     where
207         F: for<'a> FnOnce(
208                 &'a mut Store<T>,
209             ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
210             + Send
211             + 'static,
212     {
213         let engine = store.engine().clone();
214         let (in_tx, in_rx) = mpsc::channel(1);
215         let (out_tx, out_rx) = mpsc::channel(1);
216 
217         tokio::spawn(async move {
218             // Create the handler that's invoked from within the async
219             // debug-event callback.
220             let out_tx_clone = out_tx.clone();
221             let handler = Handler(Arc::new(HandlerInner {
222                 in_rx: Mutex::new(in_rx),
223                 out_tx,
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;
239             result
240         });
241 
242         Debugger {
243             engine,
244             state: DebuggerState::Initial,
245             store: None,
246             in_tx,
247             out_rx,
248         }
249     }
250 
251     /// Is the inner body done running?
252     pub fn is_complete(&self) -> bool {
253         match self.state {
254             DebuggerState::Complete => true,
255             _ => false,
256         }
257     }
258 
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 
278     /// Run the inner body until the next debug event.
279     ///
280     /// This method is cancel-safe, and no events will be lost.
281     pub async fn run(&mut self) -> Result<DebugRunResult> {
282         log::trace!("running: state is {:?}", self.state);
283 
284         self.wait_for_initial().await?;
285 
286         match self.state {
287             DebuggerState::Initial => unreachable!(),
288             DebuggerState::Paused => {
289                 log::trace!("sending Continue");
290                 self.in_tx
291                     .send(Command::Continue)
292                     .await
293                     .map_err(|_| wasmtime::format_err!("Failed to send over debug channel"))?;
294                 log::trace!("sent Continue");
295 
296                 // If that `send` was canceled, the command was not
297                 // sent, so it's fine to remain in `Paused`. If it
298                 // succeeded and we reached here, transition to
299                 // `Running` so we don't re-send.
300                 self.state = DebuggerState::Running;
301             }
302             DebuggerState::Running => {
303                 // Previous `run()` must have been canceled; no action
304                 // to take here.
305             }
306             DebuggerState::Queried => {
307                 // We expect to receive a `QueryResponse`; drop it if
308                 // the query was canceled, then transition back to
309                 // `Paused`.
310                 log::trace!("in Queried; receiving");
311                 let response =
312                     self.out_rx.recv().await.ok_or_else(|| {
313                         wasmtime::format_err!("Premature close of debugger channel")
314                     })?;
315                 log::trace!("in Queried; received, dropping");
316                 assert!(matches!(response, Response::QueryResponse(_)));
317                 self.state = DebuggerState::Paused;
318 
319                 // Now send a `Continue`, as above.
320                 log::trace!("in Paused; sending Continue");
321                 self.in_tx
322                     .send(Command::Continue)
323                     .await
324                     .map_err(|_| wasmtime::format_err!("Failed to send over debug channel"))?;
325                 self.state = DebuggerState::Running;
326             }
327             DebuggerState::Complete => {
328                 panic!("Cannot `run()` an already-complete Debugger");
329             }
330         }
331 
332         // At this point, the inner task is in Running state. We
333         // expect to receive a message when it next pauses or
334         // completes. If this `recv()` is canceled, no message is
335         // lost, and the state above accurately reflects what must be
336         // done on the next `run()`.
337         log::trace!("waiting for response");
338         let response = self
339             .out_rx
340             .recv()
341             .await
342             .ok_or_else(|| wasmtime::format_err!("Premature close of debugger channel"))?;
343 
344         match response {
345             Response::Finished(store) => {
346                 log::trace!("got Finished");
347                 self.state = DebuggerState::Complete;
348                 self.store = Some(store);
349                 Ok(DebugRunResult::Finished)
350             }
351             Response::Paused(result) => {
352                 log::trace!("got Paused");
353                 self.state = DebuggerState::Paused;
354                 Ok(result)
355             }
356             Response::QueryResponse(_) => {
357                 panic!("Invalid debug response");
358             }
359         }
360     }
361 
362     /// Run the debugger body until completion, with no further events.
363     pub async fn finish(&mut self) -> Result<()> {
364         if self.is_complete() {
365             return Ok(());
366         }
367         loop {
368             match self.run().await? {
369                 DebugRunResult::Finished => break,
370                 e => {
371                     log::trace!("finish: event {e:?}");
372                 }
373             }
374         }
375         assert!(self.is_complete());
376         Ok(())
377     }
378 
379     /// Perform some action on the contained `Store` while not running.
380     ///
381     /// This may only be invoked before the inner body finishes and
382     /// when it is paused; that is, when the `Debugger` is initially
383     /// created and after any call to `run()` returns a result other
384     /// than `DebugRunResult::Finished`. If an earlier `run()`
385     /// invocation was canceled, it must be re-invoked and return
386     /// successfully before a query is made.
387     ///
388     /// This is cancel-safe; if canceled, the result of the query will
389     /// be dropped.
390     pub async fn with_store<
391         F: FnOnce(StoreContextMut<'_, T>) -> R + Send + 'static,
392         R: Send + 'static,
393     >(
394         &mut self,
395         f: F,
396     ) -> Result<R> {
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?;
402 
403         match self.state {
404             DebuggerState::Initial => unreachable!(),
405             DebuggerState::Queried => {
406                 // Earlier query canceled; drop its response first.
407                 let response =
408                     self.out_rx.recv().await.ok_or_else(|| {
409                         wasmtime::format_err!("Premature close of debugger channel")
410                     })?;
411                 assert!(matches!(response, Response::QueryResponse(_)));
412                 self.state = DebuggerState::Paused;
413             }
414             DebuggerState::Running => {
415                 // Results from a canceled `run()`; `run()` must
416                 // complete before this can be invoked.
417                 panic!("Cannot query in Running state");
418             }
419             DebuggerState::Complete => {
420                 panic!("Cannot query when complete");
421             }
422             DebuggerState::Paused => {
423                 // OK -- this is the state we want.
424             }
425         }
426 
427         log::trace!("sending query in with_store");
428         self.in_tx
429             .send(Command::Query(Box::new(|store| Box::new(f(store)))))
430             .await
431             .map_err(|_| wasmtime::format_err!("Premature close of debugger channel"))?;
432         self.state = DebuggerState::Queried;
433 
434         let response = self
435             .out_rx
436             .recv()
437             .await
438             .ok_or_else(|| wasmtime::format_err!("Premature close of debugger channel"))?;
439         let Response::QueryResponse(resp) = response else {
440             wasmtime::bail!("Incorrect response from debugger task");
441         };
442         self.state = DebuggerState::Paused;
443 
444         Ok(*resp.downcast::<R>().expect("type mismatch"))
445     }
446 }
447 
448 /// The result of one call to `Debugger::run()`.
449 ///
450 /// This is similar to `DebugEvent` but without the lifetime, so it
451 /// can be sent across async tasks, and incorporates the possibility
452 /// of completion (`Finished`) as well.
453 #[derive(Debug)]
454 pub enum DebugRunResult {
455     /// Execution of the inner body finished.
456     Finished,
457     /// An error was raised by a hostcall.
458     HostcallError,
459     /// Wasm execution was interrupted by an epoch change.
460     EpochYield,
461     /// An exception is thrown and caught by Wasm. The current state
462     /// is at the throw-point.
463     CaughtExceptionThrown(OwnedRooted<ExnRef>),
464     /// An exception was not caught and is escaping to the host.
465     UncaughtExceptionThrown(OwnedRooted<ExnRef>),
466     /// A Wasm trap occurred.
467     Trap(Trap),
468     /// A breakpoint was reached.
469     Breakpoint,
470 }
471 
472 #[cfg(test)]
473 mod test {
474     use super::*;
475     use wasmtime::*;
476 
477     #[tokio::test]
478     #[cfg_attr(miri, ignore)]
479     async fn basic_debugger() -> wasmtime::Result<()> {
480         let _ = env_logger::try_init();
481 
482         let mut config = Config::new();
483         config.guest_debug(true);
484         let engine = Engine::new(&config)?;
485         let module = Module::new(
486             &engine,
487             r#"
488                 (module
489                   (func (export "main") (param i32 i32) (result i32)
490                     local.get 0
491                     local.get 1
492                     i32.add))
493             "#,
494         )?;
495 
496         let mut store = Store::new(&engine, ());
497         let instance = Instance::new_async(&mut store, &module, &[]).await?;
498         let main = instance.get_func(&mut store, "main").unwrap();
499 
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             })
512         });
513 
514         let event = debugger.run().await?;
515         assert!(matches!(event, DebugRunResult::Breakpoint));
516         // At (before executing) first `local.get`.
517         debugger
518             .with_store(|mut store| {
519                 let frame = store.debug_exit_frames().next().unwrap();
520                 assert_eq!(
521                     frame
522                         .wasm_function_index_and_pc(&mut store)
523                         .unwrap()
524                         .unwrap()
525                         .0
526                         .as_u32(),
527                     0
528                 );
529                 assert_eq!(
530                     frame
531                         .wasm_function_index_and_pc(&mut store)
532                         .unwrap()
533                         .unwrap()
534                         .1,
535                     36
536                 );
537                 assert_eq!(frame.num_locals(&mut store).unwrap(), 2);
538                 assert_eq!(frame.num_stacks(&mut store).unwrap(), 0);
539                 assert_eq!(frame.local(&mut store, 0).unwrap().unwrap_i32(), 1);
540                 assert_eq!(frame.local(&mut store, 1).unwrap().unwrap_i32(), 2);
541                 let frame = frame.parent(&mut store).unwrap();
542                 assert!(frame.is_none());
543             })
544             .await?;
545 
546         let event = debugger.run().await?;
547         // At second `local.get`.
548         assert!(matches!(event, DebugRunResult::Breakpoint));
549         debugger
550             .with_store(|mut store| {
551                 let frame = store.debug_exit_frames().next().unwrap();
552                 assert_eq!(
553                     frame
554                         .wasm_function_index_and_pc(&mut store)
555                         .unwrap()
556                         .unwrap()
557                         .0
558                         .as_u32(),
559                     0
560                 );
561                 assert_eq!(
562                     frame
563                         .wasm_function_index_and_pc(&mut store)
564                         .unwrap()
565                         .unwrap()
566                         .1,
567                     38
568                 );
569                 assert_eq!(frame.num_locals(&mut store).unwrap(), 2);
570                 assert_eq!(frame.num_stacks(&mut store).unwrap(), 1);
571                 assert_eq!(frame.local(&mut store, 0).unwrap().unwrap_i32(), 1);
572                 assert_eq!(frame.local(&mut store, 1).unwrap().unwrap_i32(), 2);
573                 assert_eq!(frame.stack(&mut store, 0).unwrap().unwrap_i32(), 1);
574                 let frame = frame.parent(&mut store).unwrap();
575                 assert!(frame.is_none());
576             })
577             .await?;
578 
579         let event = debugger.run().await?;
580         // At `i32.add`.
581         assert!(matches!(event, DebugRunResult::Breakpoint));
582         debugger
583             .with_store(|mut store| {
584                 let frame = store.debug_exit_frames().next().unwrap();
585                 assert_eq!(
586                     frame
587                         .wasm_function_index_and_pc(&mut store)
588                         .unwrap()
589                         .unwrap()
590                         .0
591                         .as_u32(),
592                     0
593                 );
594                 assert_eq!(
595                     frame
596                         .wasm_function_index_and_pc(&mut store)
597                         .unwrap()
598                         .unwrap()
599                         .1,
600                     40
601                 );
602                 assert_eq!(frame.num_locals(&mut store).unwrap(), 2);
603                 assert_eq!(frame.num_stacks(&mut store).unwrap(), 2);
604                 assert_eq!(frame.local(&mut store, 0).unwrap().unwrap_i32(), 1);
605                 assert_eq!(frame.local(&mut store, 1).unwrap().unwrap_i32(), 2);
606                 assert_eq!(frame.stack(&mut store, 0).unwrap().unwrap_i32(), 1);
607                 assert_eq!(frame.stack(&mut store, 1).unwrap().unwrap_i32(), 2);
608                 let frame = frame.parent(&mut store).unwrap();
609                 assert!(frame.is_none());
610             })
611             .await?;
612 
613         let event = debugger.run().await?;
614         // At return point.
615         assert!(matches!(event, DebugRunResult::Breakpoint));
616         debugger
617             .with_store(|mut store| {
618                 let frame = store.debug_exit_frames().next().unwrap();
619                 assert_eq!(
620                     frame
621                         .wasm_function_index_and_pc(&mut store)
622                         .unwrap()
623                         .unwrap()
624                         .0
625                         .as_u32(),
626                     0
627                 );
628                 assert_eq!(
629                     frame
630                         .wasm_function_index_and_pc(&mut store)
631                         .unwrap()
632                         .unwrap()
633                         .1,
634                     41
635                 );
636                 assert_eq!(frame.num_locals(&mut store).unwrap(), 2);
637                 assert_eq!(frame.num_stacks(&mut store).unwrap(), 1);
638                 assert_eq!(frame.local(&mut store, 0).unwrap().unwrap_i32(), 1);
639                 assert_eq!(frame.local(&mut store, 1).unwrap().unwrap_i32(), 2);
640                 assert_eq!(frame.stack(&mut store, 0).unwrap().unwrap_i32(), 3);
641                 let frame = frame.parent(&mut store).unwrap();
642                 assert!(frame.is_none());
643             })
644             .await?;
645 
646         // Now disable breakpoints before continuing. Second call should proceed with no more events.
647         debugger
648             .with_store(|store| {
649                 store
650                     .edit_breakpoints()
651                     .unwrap()
652                     .single_step(false)
653                     .unwrap();
654             })
655             .await?;
656 
657         let event = debugger.run().await?;
658         assert!(matches!(event, DebugRunResult::Finished));
659 
660         assert!(debugger.is_complete());
661 
662         Ok(())
663     }
664 
665     #[tokio::test]
666     #[cfg_attr(miri, ignore)]
667     async fn early_finish() -> Result<()> {
668         let _ = env_logger::try_init();
669 
670         let mut config = Config::new();
671         config.guest_debug(true);
672         let engine = Engine::new(&config)?;
673         let module = Module::new(
674             &engine,
675             r#"
676                 (module
677                   (func (export "main") (param i32 i32) (result i32)
678                     local.get 0
679                     local.get 1
680                     i32.add))
681             "#,
682         )?;
683 
684         let mut store = Store::new(&engine, ());
685         let instance = Instance::new_async(&mut store, &module, &[]).await?;
686         let main = instance.get_func(&mut store, "main").unwrap();
687 
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             })
697         });
698 
699         debugger.finish().await?;
700         assert!(debugger.is_complete());
701 
702         Ok(())
703     }
704 
705     #[tokio::test]
706     #[cfg_attr(miri, ignore)]
707     async fn drop_debugger_and_store() -> Result<()> {
708         let _ = env_logger::try_init();
709 
710         let mut config = Config::new();
711         config.guest_debug(true);
712         let engine = Engine::new(&config)?;
713         let module = Module::new(
714             &engine,
715             r#"
716                 (module
717                   (func (export "main") (param i32 i32) (result i32)
718                     local.get 0
719                     local.get 1
720                     i32.add))
721             "#,
722         )?;
723 
724         let mut store = Store::new(&engine, ());
725         let instance = Instance::new_async(&mut store, &module, &[]).await?;
726         let main = instance.get_func(&mut store, "main").unwrap();
727 
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             })
737         });
738 
739         // Step once, then drop everything at the end of this
740         // function. Wasmtime's fiber cleanup should safely happen
741         // without attempting to raise debug async handler calls with
742         // missing async context.
743         let _ = debugger.run().await?;
744 
745         Ok(())
746     }
747 }
748