Skip to content

Commit

Permalink
add basic coredump generation
Browse files Browse the repository at this point in the history
This change adds a basic coredump generation after a WebAssembly trap
was entered. The coredump includes rudimentary stack / process debugging
information.

A new CLI argument is added to enable coredump generation:
```
wasmtime --coredump-on-trap=/path/to/coredump/file module.wasm
```

See ./docs/examples-coredump.md for a working example.

Refs #5732
  • Loading branch information
xtuc committed Feb 23, 2023
1 parent f6c6bc2 commit 3d06eda
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 6 deletions.
43 changes: 40 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ wat = { workspace = true }
serde = "1.0.94"
serde_json = "1.0.26"
wasmparser = { workspace = true }
wasm-coredump-builder = { version = "0.1.10" }

[target.'cfg(unix)'.dependencies]
rustix = { workspace = true, features = ["mm", "param"] }
Expand Down
62 changes: 62 additions & 0 deletions docs/examples-coredump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Using Wasm coredump

The following steps describe how to debug using Wasm coredump in Wasmtime:

1. Compile your WebAssembly with debug info enabled; for example:

```sh
$ rustc foo.rs --target=wasm32-wasi -C debuginfo=2
```

<details>
<summary>foo.rs</summary>

fn c(v: usize) {
a(v - 3);
}

fn b(v: usize) {
c(v - 3);
}

fn a(v: usize) {
b(v - 3);
}

pub fn main() {
a(10);
}
</details>

2. Run with Wasmtime and Wasm coredump enabled:

```sh
$ wasmtime --coredump-on-trap=/tmp/coredump foo.wasm
thread 'main' panicked at 'attempt to subtract with overflow', foo.rs:10:7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Error: failed to run main module `foo.wasm`
Caused by:
0: Core dumped at /tmp/coredump
1: failed to invoke command default
2: error while executing at wasm backtrace:
...
```
3. Use [wasmgdb] to debug:
```sh
$ wasmgdb foo.wasm /tmp/coredump
wasmgdb> bt
...
#13 000175 as panic () at library/core/src/panicking.rs
#12 000010 as a (v=???) at /path/to/foo.rs
#11 000009 as c (v=???) at /path/to/foo.rs
#10 000011 as b (v=???) at /path/to/foo.rs
#9 000010 as a (v=???) at /path/to/foo.rs
#8 000012 as main () at /path/to/foo.rs
...
```
[wasmgdb]: https://crates.io/crates/wasmgdb
60 changes: 57 additions & 3 deletions src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use anyhow::{anyhow, bail, Context as _, Result};
use clap::Parser;
use once_cell::sync::Lazy;
use std::ffi::OsStr;
use std::fs::File;
use std::io::Write;
use std::path::{Component, Path, PathBuf};
use std::thread;
use std::time::Duration;
use wasmtime::{Engine, Func, Linker, Module, Store, Val, ValType};
use wasmtime::{Engine, Func, Linker, Module, Store, Val, ValType, WasmBacktrace};
use wasmtime_cli_flags::{CommonOptions, WasiModules};
use wasmtime_wasi::maybe_exit_on_error;
use wasmtime_wasi::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder};
Expand Down Expand Up @@ -151,6 +153,10 @@ pub struct RunCommand {
)]
wasm_timeout: Option<Duration>,

/// Enable coredump generation after a WebAssembly trap.
#[clap(long = "coredump-on-trap", value_name = "PATH")]
coredump_on_trap: Option<String>,

// NOTE: this must come last for trailing varargs
/// The arguments to pass to the module
#[clap(value_name = "ARGS")]
Expand All @@ -170,6 +176,13 @@ impl RunCommand {

let preopen_sockets = self.compute_preopen_sockets()?;

// Validate coredump-on-trap argument
if let Some(coredump_path) = self.coredump_on_trap.as_ref() {
if coredump_path.contains("%") {
bail!("The coredump-on-trap path does not support patterns yet.")
}
}

// Make wasi available by default.
let preopen_dirs = self.compute_preopen_dirs()?;
let argv = self.compute_argv();
Expand Down Expand Up @@ -376,13 +389,54 @@ impl RunCommand {
// Invoke the function and then afterwards print all the results that came
// out, if there are any.
let mut results = vec![Val::null(); ty.results().len()];
func.call(store, &values, &mut results).with_context(|| {
let invoke_res = func.call(store, &values, &mut results).with_context(|| {
if let Some(name) = name {
format!("failed to invoke `{}`", name)
} else {
format!("failed to invoke command default")
}
})?;
});
match invoke_res {
Ok(_) => {}
Err(err) => {
if let Some(coredump_path) = self.coredump_on_trap.as_ref() {
let bt = err
.downcast_ref::<WasmBacktrace>()
.ok_or_else(|| anyhow!("failed to downcast to WasmBacktrace"))?;

let mut coredump_builder = wasm_coredump_builder::CoredumpBuilder::new()
.executable_name(&self.module.to_str().unwrap_or_else(|| "unknown"));

{
let mut thread_builder =
wasm_coredump_builder::ThreadBuilder::new().thread_name("main");

for frame in bt.frames() {
let coredump_frame = wasm_coredump_builder::FrameBuilder::new()
.funcidx(frame.func_index())
.build();
thread_builder.add_frame(coredump_frame);
}

coredump_builder.add_thread(thread_builder.build());
}

let coredump = coredump_builder
.serialize()
.map_err(|err| anyhow!("failed to serialize coredump: {}", err))?;

let mut f = File::create(coredump_path)
.context(format!("failed to create file at `{}`", coredump_path))?;
f.write_all(&coredump)
.context("failed to write coredump file")?;

return Err(err.context(format!("Core dumped at {}", coredump_path)));
} else {
return Err(err);
}
}
};

if !results.is_empty() {
eprintln!(
"warning: using `--invoke` with a function that returns values \
Expand Down
15 changes: 15 additions & 0 deletions supply-chain/audits.toml
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,21 @@ is similar to what it once was back then. Skimming over the crate there is
nothing suspicious and it's everything you'd expect a Rust URL parser to be.
"""

[[audits.wasm-coredump-builder]]
who = "Sven Sauleau <sven@cloudflare.com>"
criteria = "safe-to-deploy"
version = "0.1.10"

[[audits.wasm-coredump-encoder]]
who = "Sven Sauleau <sven@cloudflare.com>"
criteria = "safe-to-deploy"
version = "0.1.10"

[[audits.wasm-coredump-types]]
who = "Sven Sauleau <sven@cloudflare.com>"
criteria = "safe-to-deploy"
version = "0.1.10"

[[audits.wasm-encoder]]
who = "Alex Crichton <alex@alexcrichton.com>"
criteria = "safe-to-deploy"
Expand Down
15 changes: 15 additions & 0 deletions supply-chain/imports.lock
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ criteria = "safe-to-deploy"
version = "0.12.3"
notes = "This version is used in rust's libstd, so effectively we're already trusting it"

[[audits.mozilla.audits.hermit-abi]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.1.19 -> 0.2.6"

[[audits.mozilla.audits.indexmap]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
Expand Down Expand Up @@ -238,6 +243,16 @@ criteria = "safe-to-deploy"
version = "0.2.15"
notes = "All code written or reviewed by Josh Stone."

[[audits.mozilla.audits.num_cpus]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "1.13.1 -> 1.14.0"

[[audits.mozilla.audits.num_cpus]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "1.14.0 -> 1.15.0"

[[audits.mozilla.audits.once_cell]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
Expand Down

0 comments on commit 3d06eda

Please sign in to comment.