xref: /wasmtime-44.0.1/crates/debugger/src/lib.rs (revision 73ef034a)
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                     wasmtime::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(|_| wasmtime::format_err!("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 =
281                     self.out_rx.recv().await.ok_or_else(|| {
282                         wasmtime::format_err!("Premature close of debugger channel")
283                     })?;
284                 log::trace!("in Queried; received, dropping");
285                 assert!(matches!(response, Response::QueryResponse(_)));
286                 self.state = DebuggerState::Paused;
287 
288                 // Now send a `Continue`, as above.
289                 log::trace!("in Paused; 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                 self.state = DebuggerState::Running;
295             }
296             DebuggerState::Complete => {
297                 panic!("Cannot `run()` an already-complete Debugger");
298             }
299         }
300 
301         // At this point, the inner task is in Running state. We
302         // expect to receive a message when it next pauses or
303         // completes. If this `recv()` is canceled, no message is
304         // lost, and the state above accurately reflects what must be
305         // done on the next `run()`.
306         log::trace!("waiting for response");
307         let response = self
308             .out_rx
309             .recv()
310             .await
311             .ok_or_else(|| wasmtime::format_err!("Premature close of debugger channel"))?;
312 
313         match response {
314             Response::Finished => {
315                 log::trace!("got Finished");
316                 self.state = DebuggerState::Complete;
317                 Ok(DebugRunResult::Finished)
318             }
319             Response::Paused(result) => {
320                 log::trace!("got Paused");
321                 self.state = DebuggerState::Paused;
322                 Ok(result)
323             }
324             Response::QueryResponse(_) => {
325                 panic!("Invalid debug response");
326             }
327         }
328     }
329 
330     /// Run the debugger body until completion, with no further events.
331     pub async fn finish(&mut self) -> Result<()> {
332         if self.is_complete() {
333             return Ok(());
334         }
335         loop {
336             match self.run().await? {
337                 DebugRunResult::Finished => break,
338                 e => {
339                     log::trace!("finish: event {e:?}");
340                 }
341             }
342         }
343         assert!(self.is_complete());
344         Ok(())
345     }
346 
347     /// Perform some action on the contained `Store` while not running.
348     ///
349     /// This may only be invoked before the inner body finishes and
350     /// when it is paused; that is, when the `Debugger` is initially
351     /// created and after any call to `run()` returns a result other
352     /// than `DebugRunResult::Finished`. If an earlier `run()`
353     /// invocation was canceled, it must be re-invoked and return
354     /// successfully before a query is made.
355     ///
356     /// This is cancel-safe; if canceled, the result of the query will
357     /// be dropped.
358     pub async fn with_store<
359         F: FnOnce(StoreContextMut<'_, T>) -> R + Send + 'static,
360         R: Send + 'static,
361     >(
362         &mut self,
363         f: F,
364     ) -> Result<R> {
365         assert!(!self.is_complete());
366 
367         match self.state {
368             DebuggerState::Queried => {
369                 // Earlier query canceled; drop its response first.
370                 let response =
371                     self.out_rx.recv().await.ok_or_else(|| {
372                         wasmtime::format_err!("Premature close of debugger channel")
373                     })?;
374                 assert!(matches!(response, Response::QueryResponse(_)));
375                 self.state = DebuggerState::Paused;
376             }
377             DebuggerState::Running => {
378                 // Results from a canceled `run()`; `run()` must
379                 // complete before this can be invoked.
380                 panic!("Cannot query in Running state");
381             }
382             DebuggerState::Complete => {
383                 panic!("Cannot query when complete");
384             }
385             DebuggerState::Paused => {
386                 // OK -- this is the state we want.
387             }
388         }
389 
390         self.in_tx
391             .send(Command::Query(Box::new(|store| Box::new(f(store)))))
392             .await
393             .map_err(|_| wasmtime::format_err!("Premature close of debugger channel"))?;
394         self.state = DebuggerState::Queried;
395 
396         let response = self
397             .out_rx
398             .recv()
399             .await
400             .ok_or_else(|| wasmtime::format_err!("Premature close of debugger channel"))?;
401         let Response::QueryResponse(resp) = response else {
402             wasmtime::bail!("Incorrect response from debugger task");
403         };
404         self.state = DebuggerState::Paused;
405 
406         Ok(*resp.downcast::<R>().expect("type mismatch"))
407     }
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     }
431 }
432 
433 /// The result of one call to `Debugger::run()`.
434 ///
435 /// This is similar to `DebugEvent` but without the lifetime, so it
436 /// can be sent across async tasks, and incorporates the possibility
437 /// of completion (`Finished`) as well.
438 #[derive(Debug)]
439 pub enum DebugRunResult {
440     /// Execution of the inner body finished.
441     Finished,
442     /// An error was raised by a hostcall.
443     HostcallError,
444     /// Wasm execution was interrupted by an epoch change.
445     EpochYield,
446     /// An exception is thrown and caught by Wasm. The current state
447     /// is at the throw-point.
448     CaughtExceptionThrown(OwnedRooted<ExnRef>),
449     /// An exception was not caught and is escaping to the host.
450     UncaughtExceptionThrown(OwnedRooted<ExnRef>),
451     /// A Wasm trap occurred.
452     Trap(Trap),
453     /// A breakpoint was reached.
454     Breakpoint,
455 }
456 
457 #[cfg(test)]
458 mod test {
459     use super::*;
460     use wasmtime::*;
461 
462     #[tokio::test]
463     #[cfg_attr(miri, ignore)]
464     async fn basic_debugger() -> wasmtime::Result<()> {
465         let _ = env_logger::try_init();
466 
467         let mut config = Config::new();
468         config.guest_debug(true);
469         config.async_support(true);
470         let engine = Engine::new(&config)?;
471         let module = Module::new(
472             &engine,
473             r#"
474                 (module
475                   (func (export "main") (param i32 i32) (result i32)
476                     local.get 0
477                     local.get 1
478                     i32.add))
479             "#,
480         )?;
481 
482         let mut store = Store::new(&engine, ());
483         let instance = Instance::new_async(&mut store, &module, &[]).await?;
484         let main = instance.get_func(&mut store, "main").unwrap();
485 
486         let mut debugger = Debugger::new(store, move |mut store| async move {
487             let mut results = [Val::I32(0)];
488             store.edit_breakpoints().unwrap().single_step(true).unwrap();
489             main.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results[..])
490                 .await?;
491             assert_eq!(results[0].unwrap_i32(), 3);
492             main.call_async(&mut store, &[Val::I32(3), Val::I32(4)], &mut results[..])
493                 .await?;
494             assert_eq!(results[0].unwrap_i32(), 7);
495             Ok(store)
496         });
497 
498         let event = debugger.run().await?;
499         assert!(matches!(event, DebugRunResult::Breakpoint));
500         // At (before executing) first `local.get`.
501         debugger
502             .with_store(|store| {
503                 let mut frame = store.debug_frames().unwrap();
504                 assert!(!frame.done());
505                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
506                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 36);
507                 assert_eq!(frame.num_locals(), 2);
508                 assert_eq!(frame.num_stacks(), 0);
509                 assert_eq!(frame.local(0).unwrap_i32(), 1);
510                 assert_eq!(frame.local(1).unwrap_i32(), 2);
511                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
512                 assert!(frame.done());
513             })
514             .await?;
515 
516         let event = debugger.run().await?;
517         // At second `local.get`.
518         assert!(matches!(event, DebugRunResult::Breakpoint));
519         debugger
520             .with_store(|store| {
521                 let mut frame = store.debug_frames().unwrap();
522                 assert!(!frame.done());
523                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
524                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 38);
525                 assert_eq!(frame.num_locals(), 2);
526                 assert_eq!(frame.num_stacks(), 1);
527                 assert_eq!(frame.local(0).unwrap_i32(), 1);
528                 assert_eq!(frame.local(1).unwrap_i32(), 2);
529                 assert_eq!(frame.stack(0).unwrap_i32(), 1);
530                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
531                 assert!(frame.done());
532             })
533             .await?;
534 
535         let event = debugger.run().await?;
536         // At `i32.add`.
537         assert!(matches!(event, DebugRunResult::Breakpoint));
538         debugger
539             .with_store(|store| {
540                 let mut frame = store.debug_frames().unwrap();
541                 assert!(!frame.done());
542                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
543                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 40);
544                 assert_eq!(frame.num_locals(), 2);
545                 assert_eq!(frame.num_stacks(), 2);
546                 assert_eq!(frame.local(0).unwrap_i32(), 1);
547                 assert_eq!(frame.local(1).unwrap_i32(), 2);
548                 assert_eq!(frame.stack(0).unwrap_i32(), 1);
549                 assert_eq!(frame.stack(1).unwrap_i32(), 2);
550                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
551                 assert!(frame.done());
552             })
553             .await?;
554 
555         let event = debugger.run().await?;
556         // At return point.
557         assert!(matches!(event, DebugRunResult::Breakpoint));
558         debugger
559             .with_store(|store| {
560                 let mut frame = store.debug_frames().unwrap();
561                 assert!(!frame.done());
562                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
563                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 41);
564                 assert_eq!(frame.num_locals(), 2);
565                 assert_eq!(frame.num_stacks(), 1);
566                 assert_eq!(frame.local(0).unwrap_i32(), 1);
567                 assert_eq!(frame.local(1).unwrap_i32(), 2);
568                 assert_eq!(frame.stack(0).unwrap_i32(), 3);
569                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
570                 assert!(frame.done());
571             })
572             .await?;
573 
574         // Now disable breakpoints before continuing. Second call should proceed with no more events.
575         debugger
576             .with_store(|store| {
577                 store
578                     .edit_breakpoints()
579                     .unwrap()
580                     .single_step(false)
581                     .unwrap();
582             })
583             .await?;
584 
585         let event = debugger.run().await?;
586         assert!(matches!(event, DebugRunResult::Finished));
587 
588         assert!(debugger.is_complete());
589 
590         // Ensure the store still works and the debug handler is
591         // removed.
592         let mut store = debugger.take_store().await?.unwrap();
593         let mut results = [Val::I32(0)];
594         main.call_async(&mut store, &[Val::I32(10), Val::I32(20)], &mut results[..])
595             .await?;
596         assert_eq!(results[0].unwrap_i32(), 30);
597 
598         Ok(())
599     }
600 
601     #[tokio::test]
602     #[cfg_attr(miri, ignore)]
603     async fn early_finish() -> Result<()> {
604         let _ = env_logger::try_init();
605 
606         let mut config = Config::new();
607         config.guest_debug(true);
608         config.async_support(true);
609         let engine = Engine::new(&config)?;
610         let module = Module::new(
611             &engine,
612             r#"
613                 (module
614                   (func (export "main") (param i32 i32) (result i32)
615                     local.get 0
616                     local.get 1
617                     i32.add))
618             "#,
619         )?;
620 
621         let mut store = Store::new(&engine, ());
622         let instance = Instance::new_async(&mut store, &module, &[]).await?;
623         let main = instance.get_func(&mut store, "main").unwrap();
624 
625         let mut debugger = Debugger::new(store, move |mut store| async move {
626             let mut results = [Val::I32(0)];
627             store.edit_breakpoints().unwrap().single_step(true).unwrap();
628             main.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results[..])
629                 .await?;
630             assert_eq!(results[0].unwrap_i32(), 3);
631             Ok(store)
632         });
633 
634         debugger.finish().await?;
635         assert!(debugger.is_complete());
636 
637         Ok(())
638     }
639 
640     #[tokio::test]
641     #[cfg_attr(miri, ignore)]
642     async fn drop_debugger_and_store() -> Result<()> {
643         let _ = env_logger::try_init();
644 
645         let mut config = Config::new();
646         config.guest_debug(true);
647         config.async_support(true);
648         let engine = Engine::new(&config)?;
649         let module = Module::new(
650             &engine,
651             r#"
652                 (module
653                   (func (export "main") (param i32 i32) (result i32)
654                     local.get 0
655                     local.get 1
656                     i32.add))
657             "#,
658         )?;
659 
660         let mut store = Store::new(&engine, ());
661         let instance = Instance::new_async(&mut store, &module, &[]).await?;
662         let main = instance.get_func(&mut store, "main").unwrap();
663 
664         let mut debugger = Debugger::new(store, move |mut store| async move {
665             let mut results = [Val::I32(0)];
666             store.edit_breakpoints().unwrap().single_step(true).unwrap();
667             main.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results[..])
668                 .await?;
669             assert_eq!(results[0].unwrap_i32(), 3);
670             Ok(store)
671         });
672 
673         // Step once, then drop everything at the end of this
674         // function. Wasmtime's fiber cleanup should safely happen
675         // without attempting to raise debug async handler calls with
676         // missing async context.
677         let _ = debugger.run().await?;
678 
679         Ok(())
680     }
681 }
682