I'm surprised that if I run a wasm32-wasi Rust program with wasmtime it prints the Rust backtrace (of the Wasm program, not the host) on panic. How does wasmtime implement this? I'd expect it to understand Rust stack implementation on Wasm target for this.. is this the case? Or is it Rust's runtime/core/std that prints the backtrace? If so, why is it printed even when I don't pass RUST_BACKTRACE=1
? Example:
$ cat src/main.rs
fn main() {
panic!()
}
$ cargo build --target=wasm32-wasi
Compiling wasm_backtrace v0.1.0 (/home/omer/rust/wasm_backtrace)
Finished dev [unoptimized + debuginfo] target(s) in 0.34s
$ wasmtime target/wasm32-wasi/debug/wasm_backtrace.wasm
thread 'main' panicked at 'explicit panic', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error: failed to run main module `target/wasm32-wasi/debug/wasm_backtrace.wasm`
Caused by:
0: failed to invoke command default
1: wasm trap: unreachable
wasm backtrace:
0: 0x6c34 - <unknown>!__rust_start_panic
1: 0x68df - <unknown>!rust_panic
2: 0x65b9 - <unknown>!std::panicking::rust_panic_with_hook::h2345fb0909b53e12
3: 0x10c7 - <unknown>!std::panicking::begin_panic::{{closure}}::h86678b4e45fbd7fa
4: 0x1ccc - <unknown>!std::sys_common::backtrace::__rust_end_short_backtrace::h8c7fe73fc6112e0e
5: 0xfb5 - <unknown>!std::panicking::begin_panic::h5a83b3bb18195122
6: 0x1fed - <unknown>!wasm_backtrace::g::hc5f9230a6e65a92c
7: 0x1fbf - <unknown>!wasm_backtrace::f::h80747d9c660ceee4
8: 0x1fb5 - <unknown>!wasm_backtrace::main::h9fc5985544d26b7b
9: 0x1bee - <unknown>!core::ops::function::FnOnce::call_once::hfd088d5879747b81
10: 0x1d03 - <unknown>!std::sys_common::backtrace::__rust_begin_short_backtrace::h2d7439e32125816a
11: 0x4a5 - <unknown>!std::rt::lang_start::{{closure}}::hc9be9987846fa67a
12: 0x69f0 - <unknown>!std::rt::lang_start_internal::h260050c92cd470af
13: 0x457 - <unknown>!std::rt::lang_start::hd15e4b73e676a12b
14: 0x200b - <unknown>!__original_main
15: 0x397 - <unknown>!_start
It is generated and printed by wasmtime. During the trap, cranelift gets entire backtrace (see backtrace crate) and filters out only wasm addresses, wasmtime just prints out wasm portion, but capable of getting entire stack including host.
cranelift gets entire backtrace (see backtrace crate)
How is this done exactly? Does wasmtime/cranelift know about Rust's stack implementation in Wasm linear memory?
So backtrace
(the crate) can walk Rust stack.. but for example in my example wasmtime needs to be aware that the Wasm program is Rust and call that crate to obtain the stack trace, no?
nope, the wasm program can be in C, and your will see similar trace but with C names
So wasmtime understands C ABI on Wasm?
backtrace walk native stack not just rust
it is more related with libunwind works than C API
OK thanks. I'll look at uses of the backtrace crate in wasmtime for details.
One more question: the backtrace in my example is obtained on Wasm trap instruction, right?
more exactly from unreachable
(multiple instructions can trap for different reasons)
@Yury Delendik thanks, really helpful. Does wasmtime provide an API for getting the Wasm program's backtrace (using the backtrace
crate or otherwise) when implementing a host function? As far as I understand host functions have access to Caller
but it's not possible to get a backtrace from that type, right?
https://docs.wasmtime.dev/api/wasmtime/struct.Trap.html ?
On a related note, I think we should probably default the module name to the filename sans .wasm
so that backtraces aren't filled with <unknown>
.
Filed https://github.com/bytecodealliance/wasmtime/issues/2495
Last updated: Dec 23 2024 at 12:05 UTC