Skip to content

Commit 5c1557d

Browse files
authored
Add docs and example for debugging with core dumps (bytecodealliance#7087)
* Add docs for debugging with core dumps * Fix reference to old style CLI flag * Add `no_run` to example that is only there to trap
1 parent b1ed9b3 commit 5c1557d

File tree

7 files changed

+261
-37
lines changed

7 files changed

+261
-37
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ foo
2121
publish
2222
vendor
2323
examples/build
24+
*.coredump

docs/SUMMARY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- [Introduction](./introduction.md)
44
- [Examples](./examples.md)
55
- [Debugging WebAssembly](./examples-debugging.md)
6+
- [Debugging with `gdb` and `lldb`](./examples-debugging-native-debugger.md)
7+
- [Debugging with Core Dumps](./examples-debugging-core-dumps.md)
68
- [Profiling WebAssembly](./examples-profiling.md)
79
- [Profiling with Perf](./examples-profiling-perf.md)
810
- [Profiling with VTune](./examples-profiling-vtune.md)
@@ -15,6 +17,7 @@
1517
- [WASI](./examples-rust-wasi.md)
1618
- [Linking Modules](./examples-rust-linking.md)
1719
- [Debugging](./examples-rust-debugging.md)
20+
- [Core Dumps](./examples-rust-core-dumps.md)
1821
- [Using Multi-Value](./examples-rust-multi-value.md)
1922
- [Embedding in C](./examples-c-embed.md)
2023
- [Hello, World!](./examples-c-hello-world.md)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Debugging WebAssembly with Core Dumps
2+
3+
Wasmtime can be configured to generate [the standard Wasm core dump
4+
format][spec] whenever guest Wasm programs trap. These core dumps can then be
5+
consumed by external tooling (such as [`wasmgdb`][wasmgdb]) for post-mortem analysis.
6+
7+
This page focuses on generating and inspecting core dumps via the Wasmtime
8+
command-line interface. For details on how to generate core dumps via the
9+
`wasmtime` embedding API, see [Core Dumps in a Rust
10+
Embedding](./examples-rust-core-dumps.md).
11+
12+
First, we need to compile some code to Wasm that can trap. Consider the
13+
following Rust code:
14+
15+
```rust,no_run
16+
// trap.rs
17+
18+
fn main() {
19+
foo(42);
20+
}
21+
22+
fn foo(x: u32) {
23+
bar(x);
24+
}
25+
26+
fn bar(x: u32) {
27+
baz(x);
28+
}
29+
30+
fn baz(x: u32) {
31+
assert!(x != 42);
32+
}
33+
```
34+
35+
We can compile it to Wasm with the following command:
36+
37+
```shell-session
38+
$ rustc --target wasm32-wasi -o ./trap.wasm ./trap.rs
39+
```
40+
41+
Next, we can run it in Wasmtime and capture a core dump when it traps:
42+
43+
```shell-session
44+
$ wasmtime -D coredump=./trap.coredump ./trap.wasm
45+
thread 'main' panicked at /home/nick/scratch/trap.rs:14:5:
46+
assertion failed: x != 42
47+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
48+
Error: failed to run main module `/home/nick/scratch/trap.wasm`
49+
50+
Caused by:
51+
0: core dumped at /home/nick/scratch/trap.coredump
52+
1: failed to invoke command default
53+
2: wasm coredump generated while executing store_name:
54+
modules:
55+
<module>
56+
instances:
57+
Instance(store=1, index=1)
58+
memories:
59+
Memory(store=1, index=1)
60+
globals:
61+
Global(store=1, index=0)
62+
backtrace:
63+
error while executing at wasm backtrace:
64+
0: 0x5961 - <unknown>!__rust_start_panic
65+
1: 0x562a - <unknown>!rust_panic
66+
2: 0x555d - <unknown>!std::panicking::rust_panic_with_hook::h58e7d0b3d70e95b6
67+
3: 0x485d - <unknown>!std::panicking::begin_panic_handler::{{closure}}::h1853004619879cfd
68+
4: 0x47bd - <unknown>!std::sys_common::backtrace::__rust_end_short_backtrace::hed32bc5557405634
69+
5: 0x4f02 - <unknown>!rust_begin_unwind
70+
6: 0xac01 - <unknown>!core::panicking::panic_fmt::h53ca5bf48b428895
71+
7: 0xb1c5 - <unknown>!core::panicking::panic::h62c2c2bb054da7e1
72+
8: 0x661 - <unknown>!trap::baz::h859f39b65389c077
73+
9: 0x616 - <unknown>!trap::bar::h7ad12f9c5b730d17
74+
10: 0x60a - <unknown>!trap::foo::ha69c95723611c1a0
75+
11: 0x5fe - <unknown>!trap::main::hdfcd9f2d150fc3dc
76+
12: 0x434 - <unknown>!core::ops::function::FnOnce::call_once::h24336e950fb97d1e
77+
13: 0x40b - <unknown>!std::sys_common::backtrace::__rust_begin_short_backtrace::h2b37384d2b1a57ff
78+
14: 0x4ec - <unknown>!std::rt::lang_start::{{closure}}::he86eb1b6ac6d7501
79+
15: 0x24f7 - <unknown>!std::rt::lang_start_internal::h21f6a1d8f3633b54
80+
16: 0x497 - <unknown>!std::rt::lang_start::h7d256f21902ff32b
81+
17: 0x687 - <unknown>!__main_void
82+
18: 0x3e6 - <unknown>!_start
83+
note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
84+
```
85+
86+
You now have a core dump at `./trap.coredump` that can be consumed by external
87+
tooling to do post-mortem analysis of the failure.
88+
89+
[spec]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md
90+
[wasmgdb]: https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Debugging with `gdb` and `lldb`
2+
3+
The following steps describe how to use `gdb` or `lldb` to debug both the Wasm
4+
guest and the host (i.e. the Wasmtime CLI or your Wasmtime-embedding program) at
5+
the same time:
6+
7+
1. Compile your WebAssembly with debug info enabled, usually `-g`; for
8+
example:
9+
10+
```sh
11+
clang foo.c -g -o foo.wasm
12+
```
13+
14+
2. Run Wasmtime with the debug info enabled; this is `-D debug-info` from the
15+
CLI and `Config::debug_info(true)` in an embedding (e.g. see [debugging in a
16+
Rust embedding](./examples-rust-debugging.md))
17+
18+
3. Use a supported debugger:
19+
20+
```sh
21+
lldb -- wasmtime run -D debug-info foo.wasm
22+
```
23+
```sh
24+
gdb --args wasmtime run -D debug-info foo.wasm
25+
```
26+
27+
If you run into trouble, the following discussions might help:
28+
29+
- On MacOS with LLDB you may need to run: `settings set
30+
plugin.jit-loader.gdb.enable on`
31+
([#1953](https://github.com/bytecodealliance/wasmtime/issues/1953))
32+
33+
- With LLDB, call `__vmctx.set()` to set the current context before calling any
34+
dereference operators
35+
([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)):
36+
```sh
37+
(lldb) p __vmctx->set()
38+
(lldb) p *foo
39+
```
40+
41+
- The address of the start of instance memory can be found in `__vmctx->memory`
42+
43+
- On Windows you may experience degraded WASM compilation throughput due to the
44+
enablement of additional native heap checks when under the debugger by default.
45+
You can set the environment variable `_NO_DEBUG_HEAP` to `1` to disable them.

docs/examples-debugging.md

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,11 @@
11
# Debugging WebAssembly
22

3-
The following steps describe a common way to debug a WebAssembly module in
4-
Wasmtime:
3+
Wasmtime currently provides the following support for debugging misbehaving
4+
WebAssembly:
55

6-
1. Compile your WebAssembly with debug info enabled, usually `-g`; for
7-
example:
6+
* We can [live debug and step through the guest Wasm and the host at the same
7+
time with `gdb` or `lldb`.](./examples-debugging-native-debugger.md)
88

9-
```sh
10-
clang foo.c -g -o foo.wasm
11-
```
12-
13-
2. Run Wasmtime with the debug info enabled; this is `-g` from the CLI and
14-
`Config::debug_info(true)` in an embedding (e.g. see [debugging in a Rust
15-
embedding](./examples-rust-debugging.md))
16-
17-
3. Use a supported debugger:
18-
19-
```sh
20-
lldb -- wasmtime run -D debug-info foo.wasm
21-
```
22-
```sh
23-
gdb --args wasmtime run -D debug-info foo.wasm
24-
```
25-
26-
If you run into trouble, the following discussions might help:
27-
28-
- On MacOS with LLDB you may need to run: `settings set
29-
plugin.jit-loader.gdb.enable on`
30-
([#1953](https://github.com/bytecodealliance/wasmtime/issues/1953))
31-
- With LLDB, call `__vmctx.set()` to set the current context before calling any
32-
dereference operators
33-
([#1482](https://github.com/bytecodealliance/wasmtime/issues/1482)):
34-
```sh
35-
(lldb) p __vmctx->set()
36-
(lldb) p *foo
37-
```
38-
- The address of the start of instance memory can be found in `__vmctx->memory`
39-
- On Windows you may experience degraded WASM compilation throughput due to the
40-
enablement of additional native heap checks when under the debugger by default.
41-
You can set the environment variable `_NO_DEBUG_HEAP` to `1` to disable them.
9+
* When a Wasm guest traps, we can [generate Wasm core
10+
dumps](./examples-debugging-core-dumps.md), that can be consumed by other
11+
tools for post-mortem analysis.

docs/examples-rust-core-dumps.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Core Dumps
2+
3+
You can also [browse this source code online][code] and clone the wasmtime
4+
repository to run the example locally.
5+
6+
[code]: https://github.com/bytecodealliance/wasmtime/blob/main/examples/fib-debug/main.rs
7+
8+
This examples shows how to configure capturing [core dumps] when a Wasm guest
9+
traps that can then be passed to external tools (like [`wasmgdb`]) for
10+
post-mortem analysis.
11+
12+
[core dumps]: https://github.com/WebAssembly/tool-conventions/blob/main/Coredump.md
13+
[`wasmgdb`]: https://github.com/xtuc/wasm-coredump/blob/main/bin/wasmgdb/README.md
14+
15+
## `main.rs`
16+
17+
```rust,ignore
18+
{{#include ../examples/coredump.rs}}
19+
```

examples/coredump.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//! An example of how to configure capturing core dumps when the guest Wasm
2+
//! traps that can then be passed to external tools for post-mortem analysis.
3+
4+
// You can execute this example with `cargo run --example coredump`.
5+
6+
use anyhow::Result;
7+
use wasmtime::*;
8+
9+
fn main() -> Result<()> {
10+
println!("Configure core dumps to be captured on trap.");
11+
let mut config = Config::new();
12+
config.coredump_on_trap(true);
13+
let engine = Engine::new(&config)?;
14+
let mut store = Store::new(&engine, ());
15+
16+
println!("Define a Wasm module that will mutate local state and then trap.");
17+
let module = Module::new(
18+
store.engine(),
19+
r#"
20+
(module $trapper
21+
(memory 10)
22+
(global $g (mut i32) (i32.const 0))
23+
24+
(func (export "run")
25+
call $a
26+
)
27+
28+
(func $a
29+
i32.const 0x1234
30+
i64.const 42
31+
i64.store
32+
call $b
33+
)
34+
35+
(func $b
36+
i32.const 36
37+
global.set $g
38+
call $c
39+
)
40+
41+
(func $c
42+
unreachable
43+
)
44+
)
45+
"#,
46+
)?;
47+
48+
println!("Instantiate the module.");
49+
let instance = Instance::new(&mut store, &module, &[])?;
50+
51+
println!("Invoke its 'run' function.");
52+
let run = instance
53+
.get_func(&mut store, "run")
54+
.expect("should have 'run' export");
55+
let args = &[];
56+
let results = &mut [];
57+
let ok = run.call(&mut store, args, results);
58+
59+
println!("Calling that function trapped.");
60+
assert!(ok.is_err());
61+
let err = ok.unwrap_err();
62+
assert!(err.is::<Trap>());
63+
64+
println!("Extract the captured core dump.");
65+
let dump = err
66+
.downcast_ref::<WasmCoreDump>()
67+
.expect("should have an attached core dump, since we configured core dumps on");
68+
69+
println!(
70+
"Number of memories in the core dump: {}",
71+
dump.memories().len()
72+
);
73+
for (i, mem) in dump.memories().iter().enumerate() {
74+
if let Some(addr) = mem.data(&store).iter().position(|byte| *byte != 0) {
75+
let val = mem.data(&store)[addr];
76+
println!(" First nonzero byte for memory {i}: {val} @ {addr:#x}");
77+
} else {
78+
println!(" Memory {i} is all zeroes.");
79+
}
80+
}
81+
82+
println!(
83+
"Number of globals in the core dump: {}",
84+
dump.globals().len()
85+
);
86+
for (i, global) in dump.globals().iter().enumerate() {
87+
let val = global.get(&mut store);
88+
println!(" Global {i} = {val:?}");
89+
}
90+
91+
println!("Serialize the core dump and write it to ./example.coredump");
92+
let serialized = dump.serialize(&mut store, "trapper.wasm");
93+
std::fs::write("./example.coredump", serialized)?;
94+
95+
Ok(())
96+
}

0 commit comments

Comments
 (0)