Skip to content

Instantly share code, notes, and snippets.

@alexcrichton
Created October 22, 2025 15:21
Show Gist options
  • Select an option

  • Save alexcrichton/0e87ad78de92f7ada9112d34e1012f8e to your computer and use it in GitHub Desktop.

Select an option

Save alexcrichton/0e87ad78de92f7ada9112d34e1012f8e to your computer and use it in GitHub Desktop.
diff --git a/crates/wasmtime/src/runtime/debug.rs b/crates/wasmtime/src/runtime/debug.rs
index 0977bf8bc8..374e3d11c5 100644
--- a/crates/wasmtime/src/runtime/debug.rs
+++ b/crates/wasmtime/src/runtime/debug.rs
@@ -3,11 +3,9 @@
use crate::{
AnyRef, AsContext, AsContextMut, ExnRef, ExternRef, Func, Instance, Module, OwnedRooted,
StoreContext, StoreContextMut, Val,
- prelude::Box,
store::{AutoAssertNoGc, StoreOpaque},
vm::{CurrentActivationBacktrace, VMContext},
};
-use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use core::{ffi::c_void, ptr::NonNull};
@@ -451,9 +449,9 @@ impl<'a, T: 'static> AsContextMut for DebugFrameCursor<'a, T> {
/// One debug event that occurs when running Wasm code on a store with
/// a debug handler attached.
#[derive(Debug)]
-pub enum DebugEvent {
+pub enum DebugEvent<'a> {
/// An `anyhow::Error` was raised by a hostcall.
- HostcallError,
+ HostcallError(&'a anyhow::Error),
/// An exception is thrown and caught by Wasm. The current state
/// is at the throw-point.
CaughtExceptionThrown(OwnedRooted<ExnRef>),
@@ -499,15 +497,15 @@ pub enum DebugEvent {
/// another async stack, and the stack that polls the future
/// running a particular Wasm invocation could change after each
/// suspend point in the handler.
-pub trait DebugHandler: Send + Sync + 'static {
+pub trait DebugHandler: Clone + Send + Sync + 'static {
/// The data expected on the store that this handler is attached
/// to.
type Data;
/// Handle a debug event.
- fn handle<'a>(
- self: Arc<Self>,
- store: StoreContextMut<'a, Self::Data>,
- event: DebugEvent,
- ) -> Box<dyn Future<Output = ()> + Send + 'a>;
+ fn handle(
+ &self,
+ store: StoreContextMut<'_, Self::Data>,
+ event: DebugEvent<'_>,
+ ) -> impl Future<Output = ()> + Send;
}
diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs
index e44eccd0bc..894f1ec5a4 100644
--- a/crates/wasmtime/src/runtime/store.rs
+++ b/crates/wasmtime/src/runtime/store.rs
@@ -272,7 +272,34 @@ pub struct StoreInner<T: 'static> {
/// `StoreContextMut`); so we need to hold a separate reference to
/// it while invoking it.
#[cfg(feature = "debug")]
- pub(crate) debug_handler: Option<Arc<dyn DebugHandler<Data = T>>>,
+ debug_handler: Option<Box<dyn StoreDebugHandler<T>>>,
+}
+
+// Internal adapter from `DebugHandler` to something object-safe.
+#[cfg(feature = "debug")]
+trait StoreDebugHandler<T: 'static>: Send + Sync {
+ fn handle<'a>(
+ self: Box<Self>,
+ store: StoreContextMut<'a, T>,
+ event: crate::DebugEvent<'a>,
+ ) -> Box<dyn Future<Output = ()> + Send + 'a>;
+}
+
+#[cfg(feature = "debug")]
+impl<D> StoreDebugHandler<D::Data> for D
+where
+ D: DebugHandler,
+ D::Data: Send,
+{
+ fn handle<'a>(
+ self: Box<Self>,
+ store: StoreContextMut<'a, D::Data>,
+ event: crate::DebugEvent<'a>,
+ ) -> Box<dyn Future<Output = ()> + Send + 'a> {
+ let handler: D = (*self).clone();
+ store.0.debug_handler = Some(self);
+ Box::new(async move { handler.handle(store, event).await })
+ }
}
enum ResourceLimiterInner<T> {
@@ -1250,7 +1277,7 @@ impl<T> Store<T> {
self.engine().tunables().debug_guest,
"debug hooks require guest debugging to be enabled"
);
- self.inner.debug_handler = Some(Arc::new(handler));
+ self.inner.debug_handler = Some(Box::new(handler));
}
/// Clear the debug handler on this store. If any existed, it will
@@ -2720,10 +2747,12 @@ unsafe impl<T> VMStore for StoreInner<T> {
}
#[cfg(feature = "debug")]
- fn block_on_debug_handler(&mut self, event: crate::DebugEvent) -> anyhow::Result<()> {
- if let Some(handler) = self.debug_handler.as_ref().cloned() {
+ fn block_on_debug_handler(&mut self, event: crate::DebugEvent<'_>) -> anyhow::Result<()> {
+ if let Some(handler) = self.debug_handler.take() {
log::trace!("about to raise debug event {event:?}");
- StoreContextMut(self).block_on(|store| Pin::from(handler.handle(store, event)))
+ StoreContextMut(self).with_blocking(|store, cx| {
+ cx.block_on(Pin::from(handler.handle(store, event)).as_mut())
+ })
} else {
Ok(())
}
diff --git a/crates/wasmtime/src/runtime/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs
index c4733efbcf..353f8e6083 100644
--- a/crates/wasmtime/src/runtime/vm/traphandlers.rs
+++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs
@@ -857,9 +857,9 @@ impl CallThreadState {
..
} => store.block_on_debug_handler(crate::DebugEvent::Trap(*trap)),
UnwindState::UnwindToHost {
- reason: UnwindReason::Trap(TrapReason::User(_)),
+ reason: UnwindReason::Trap(TrapReason::User(err)),
..
- } => store.block_on_debug_handler(crate::DebugEvent::HostcallError),
+ } => store.block_on_debug_handler(crate::DebugEvent::HostcallError(err)),
UnwindState::UnwindToHost {
reason: UnwindReason::Trap(TrapReason::Jit { .. }),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment