Skip to content

Commit 69b130d

Browse files
authored
Debugger: add top-level crate and async wrapper allowing for a "debugger environment". (#12183)
* Debugger: add top-level crate and async wrapper allowing for a "debug environment". The debug support in Wasmtime so far is structured around async callbacks that occur at certain kinds of events, like breakpoints. This is a suitable foundation but makes for an awkward implementation of a top-level debugger implementation, which likely has an event loop dealing with user commands (via a UI or a protocol connection) and expects to perform actions such as "run until next breakpoint". This PR introduces a new crate that wraps a `Store` in a `Debugger`. This wrapper embodies an inner async body that can perform whatever actions it likes on the `Store` that is passed back in. This inner body is spawned as an async task. The debugger wrapper registers its own `DebugHandler` callback that communicates with the outside world via bidirectional command/response queues. On the "outside", the `Debugger` presents an interface suitable for inserting into a debug protocol server or UI: an async method that runs until next event and returns that event, and a method that permits querying or modifying the store whenever the `run` method is not executing. The latter operates by sending a closure over the queue, because the `Store` must continue to be owned by the async task that is (still) running and suspended in async callbacks. Right now, this is exercised only via a few unit tests, but the intent is to next build up the "top half" of the debugger using this abstraction, e.g. by running a gdbstub protocol server (likely as a Wasm component in a "debug-main WIT world" -- RFC needed for this). Also, when we eventually move debugging over to native use of `run_concurrent`, this paradigm should remain mostly unchanged at this level of API: there can still be an object that has an async method that runs and yields the next event, and there can still be a method that takes a closure that can operate (within its scope only) on the `Store`. A few warts that I could use feedback on: - Cancelation safety is weird. Fibers panic when dropped before execution of their body completes, and this seems to mean that we can't allow a `Debugger` to drop early (or at least, the `tokio::test` unit test that owns the runtime that runs the async task to finish before the debugged body completes!). If there is a better way to handle cancelation safety here, I'm all ears. - It's not clear to me if the boxed-closure-and-`Any` approach to providing access to the `Store` is the best we can do, but I suspect it is. * Cancel safety! * Add new crate to publish.rs script. * Review feedback. * Review feedback: state diagram. * Update after merge from main: new DebugEvent for epoch yields. * Make Debugger drop-safe by making debug event callbacks compatible with fiber teardown.
1 parent 7da79ce commit 69b130d

File tree

7 files changed

+731
-1
lines changed

7 files changed

+731
-1
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ members = [
164164
"crates/wasi-preview1-component-adapter",
165165
"crates/wasi-preview1-component-adapter/verify",
166166
"crates/wasi-tls-nativetls",
167+
"crates/debugger",
167168
"crates/wizer/fuzz",
168169
"crates/wizer/tests/regex-test",
169170
"crates/wizer/benches/regex-bench",
@@ -276,6 +277,7 @@ wasmtime-jit-icache-coherence = { path = "crates/jit-icache-coherence", version
276277
wasmtime-wit-bindgen = { path = "crates/wit-bindgen", version = "=41.0.0", package = 'wasmtime-internal-wit-bindgen' }
277278
wasmtime-math = { path = "crates/math", version = "=41.0.0", package = 'wasmtime-internal-math' }
278279
wasmtime-unwinder = { path = "crates/unwinder", version = "=41.0.0", package = 'wasmtime-internal-unwinder' }
280+
wasmtime-debugger = { path = "crates/debugger", version = "=41.0.0", package = "wasmtime-internal-debugger" }
279281
wasmtime-wizer = { path = "crates/wizer", version = "41.0.0" }
280282

281283
# Miscellaneous crates without a `wasmtime-*` prefix in their name but still

crates/debugger/Cargo.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[package]
2+
name = "wasmtime-internal-debugger"
3+
authors.workspace = true
4+
version.workspace = true
5+
description = "INTERNAL: Wasmtime's guest-debugger functionality"
6+
license = "Apache-2.0 WITH LLVM-exception"
7+
documentation = "https://docs.rs/wasmtime-debugger"
8+
repository = "https://github.com/bytecodealliance/wasmtime"
9+
readme = "README.md"
10+
keywords = ["debugger"]
11+
edition.workspace = true
12+
rust-version.workspace = true
13+
14+
[lints]
15+
workspace = true
16+
17+
[dependencies]
18+
wasmtime = { workspace = true, features = ["debug", "std", "async"] }
19+
tokio = { workspace = true, features = ["rt", "sync", "macros"] }
20+
anyhow = { workspace = true }
21+
log = { workspace = true }
22+
23+
[dev-dependencies]
24+
# Depend on `wasmtime` again to get `cranelift` and `wat` so we can
25+
# write unit tests that build modules from textual WAT.
26+
wasmtime = { workspace = true, features = ["cranelift", "wat"] }
27+
env_logger = { workspace = true }
28+
29+
[features]
30+
default = []

0 commit comments

Comments
 (0)