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