xref: /wasmtime-44.0.1/crates/debugger/src/lib.rs (revision 9acefdfe)
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         let engine = Engine::new(&config)?;
470         let module = Module::new(
471             &engine,
472             r#"
473                 (module
474                   (func (export "main") (param i32 i32) (result i32)
475                     local.get 0
476                     local.get 1
477                     i32.add))
478             "#,
479         )?;
480 
481         let mut store = Store::new(&engine, ());
482         let instance = Instance::new_async(&mut store, &module, &[]).await?;
483         let main = instance.get_func(&mut store, "main").unwrap();
484 
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)
495         });
496 
497         let event = debugger.run().await?;
498         assert!(matches!(event, DebugRunResult::Breakpoint));
499         // At (before executing) first `local.get`.
500         debugger
501             .with_store(|store| {
502                 let mut frame = store.debug_frames().unwrap();
503                 assert!(!frame.done());
504                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
505                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 36);
506                 assert_eq!(frame.num_locals(), 2);
507                 assert_eq!(frame.num_stacks(), 0);
508                 assert_eq!(frame.local(0).unwrap_i32(), 1);
509                 assert_eq!(frame.local(1).unwrap_i32(), 2);
510                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
511                 assert!(frame.done());
512             })
513             .await?;
514 
515         let event = debugger.run().await?;
516         // At second `local.get`.
517         assert!(matches!(event, DebugRunResult::Breakpoint));
518         debugger
519             .with_store(|store| {
520                 let mut frame = store.debug_frames().unwrap();
521                 assert!(!frame.done());
522                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
523                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 38);
524                 assert_eq!(frame.num_locals(), 2);
525                 assert_eq!(frame.num_stacks(), 1);
526                 assert_eq!(frame.local(0).unwrap_i32(), 1);
527                 assert_eq!(frame.local(1).unwrap_i32(), 2);
528                 assert_eq!(frame.stack(0).unwrap_i32(), 1);
529                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
530                 assert!(frame.done());
531             })
532             .await?;
533 
534         let event = debugger.run().await?;
535         // At `i32.add`.
536         assert!(matches!(event, DebugRunResult::Breakpoint));
537         debugger
538             .with_store(|store| {
539                 let mut frame = store.debug_frames().unwrap();
540                 assert!(!frame.done());
541                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
542                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 40);
543                 assert_eq!(frame.num_locals(), 2);
544                 assert_eq!(frame.num_stacks(), 2);
545                 assert_eq!(frame.local(0).unwrap_i32(), 1);
546                 assert_eq!(frame.local(1).unwrap_i32(), 2);
547                 assert_eq!(frame.stack(0).unwrap_i32(), 1);
548                 assert_eq!(frame.stack(1).unwrap_i32(), 2);
549                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
550                 assert!(frame.done());
551             })
552             .await?;
553 
554         let event = debugger.run().await?;
555         // At return point.
556         assert!(matches!(event, DebugRunResult::Breakpoint));
557         debugger
558             .with_store(|store| {
559                 let mut frame = store.debug_frames().unwrap();
560                 assert!(!frame.done());
561                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().0.as_u32(), 0);
562                 assert_eq!(frame.wasm_function_index_and_pc().unwrap().1, 41);
563                 assert_eq!(frame.num_locals(), 2);
564                 assert_eq!(frame.num_stacks(), 1);
565                 assert_eq!(frame.local(0).unwrap_i32(), 1);
566                 assert_eq!(frame.local(1).unwrap_i32(), 2);
567                 assert_eq!(frame.stack(0).unwrap_i32(), 3);
568                 assert_eq!(frame.move_to_parent(), FrameParentResult::SameActivation);
569                 assert!(frame.done());
570             })
571             .await?;
572 
573         // Now disable breakpoints before continuing. Second call should proceed with no more events.
574         debugger
575             .with_store(|store| {
576                 store
577                     .edit_breakpoints()
578                     .unwrap()
579                     .single_step(false)
580                     .unwrap();
581             })
582             .await?;
583 
584         let event = debugger.run().await?;
585         assert!(matches!(event, DebugRunResult::Finished));
586 
587         assert!(debugger.is_complete());
588 
589         // Ensure the store still works and the debug handler is
590         // removed.
591         let mut store = debugger.take_store().await?.unwrap();
592         let mut results = [Val::I32(0)];
593         main.call_async(&mut store, &[Val::I32(10), Val::I32(20)], &mut results[..])
594             .await?;
595         assert_eq!(results[0].unwrap_i32(), 30);
596 
597         Ok(())
598     }
599 
600     #[tokio::test]
601     #[cfg_attr(miri, ignore)]
602     async fn early_finish() -> Result<()> {
603         let _ = env_logger::try_init();
604 
605         let mut config = Config::new();
606         config.guest_debug(true);
607         let engine = Engine::new(&config)?;
608         let module = Module::new(
609             &engine,
610             r#"
611                 (module
612                   (func (export "main") (param i32 i32) (result i32)
613                     local.get 0
614                     local.get 1
615                     i32.add))
616             "#,
617         )?;
618 
619         let mut store = Store::new(&engine, ());
620         let instance = Instance::new_async(&mut store, &module, &[]).await?;
621         let main = instance.get_func(&mut store, "main").unwrap();
622 
623         let mut debugger = Debugger::new(store, move |mut store| async move {
624             let mut results = [Val::I32(0)];
625             store.edit_breakpoints().unwrap().single_step(true).unwrap();
626             main.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results[..])
627                 .await?;
628             assert_eq!(results[0].unwrap_i32(), 3);
629             Ok(store)
630         });
631 
632         debugger.finish().await?;
633         assert!(debugger.is_complete());
634 
635         Ok(())
636     }
637 
638     #[tokio::test]
639     #[cfg_attr(miri, ignore)]
640     async fn drop_debugger_and_store() -> Result<()> {
641         let _ = env_logger::try_init();
642 
643         let mut config = Config::new();
644         config.guest_debug(true);
645         let engine = Engine::new(&config)?;
646         let module = Module::new(
647             &engine,
648             r#"
649                 (module
650                   (func (export "main") (param i32 i32) (result i32)
651                     local.get 0
652                     local.get 1
653                     i32.add))
654             "#,
655         )?;
656 
657         let mut store = Store::new(&engine, ());
658         let instance = Instance::new_async(&mut store, &module, &[]).await?;
659         let main = instance.get_func(&mut store, "main").unwrap();
660 
661         let mut debugger = Debugger::new(store, move |mut store| async move {
662             let mut results = [Val::I32(0)];
663             store.edit_breakpoints().unwrap().single_step(true).unwrap();
664             main.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results[..])
665                 .await?;
666             assert_eq!(results[0].unwrap_i32(), 3);
667             Ok(store)
668         });
669 
670         // Step once, then drop everything at the end of this
671         // function. Wasmtime's fiber cleanup should safely happen
672         // without attempting to raise debug async handler calls with
673         // missing async context.
674         let _ = debugger.run().await?;
675 
676         Ok(())
677     }
678 }
679