Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add basic coredump generation #3626

Merged
merged 1 commit into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Cargo.lock

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

62 changes: 62 additions & 0 deletions docs/en/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 Wasmer:

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 Wasmer and Wasm coredump enabled:

```sh
$ wasmer --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
1 change: 1 addition & 0 deletions lib/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ rpassword = "7.2.0"
pathdiff = "0.2.1"
sha2 = "0.10.6"
object = "0.30.0"
wasm-coredump-builder = { version = "0.1.11" }

[build-dependencies]
chrono = { version = "^0.4", default-features = false, features = [ "std", "clock" ] }
Expand Down
70 changes: 68 additions & 2 deletions lib/cli/src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::suggestions::suggest_function_exports;
use crate::warning;
use anyhow::{anyhow, Context, Result};
use clap::Parser;
use std::fs::File;
use std::io::Write;
use std::ops::Deref;
use std::path::PathBuf;
#[cfg(feature = "cache")]
Expand Down Expand Up @@ -89,6 +91,10 @@ pub struct RunWithoutFile {
#[clap(long = "verbose")]
pub(crate) verbose: Option<u8>,

/// Enable coredump generation after a WebAssembly trap.
#[clap(name = "COREDUMP PATH", long = "coredump-on-trap", parse(from_os_str))]
coredump_on_trap: Option<PathBuf>,

/// Application arguments
#[clap(value_name = "ARGS")]
pub(crate) args: Vec<String>,
Expand Down Expand Up @@ -154,7 +160,7 @@ impl RunWithPathBuf {
if self.debug {
logging::set_up_logging(self_clone.verbose.unwrap_or(0)).unwrap();
}
self_clone.inner_execute().with_context(|| {
let invoke_res = self_clone.inner_execute().with_context(|| {
format!(
"failed to run `{}`{}",
self_clone.path.display(),
Expand All @@ -164,7 +170,23 @@ impl RunWithPathBuf {
""
}
)
})
});

if let Err(err) = invoke_res {
if let Some(coredump_path) = self.coredump_on_trap.as_ref() {
let source_name = self.path.to_str().unwrap_or_else(|| "unknown");
if let Err(coredump_err) = generate_coredump(&err, &source_name, &coredump_path) {
eprintln!("warning: coredump failed to generate: {}", coredump_err);
Err(err)
} else {
Err(err.context(format!("core dumped at {}", coredump_path.display())))
}
} else {
Err(err)
}
} else {
invoke_res
}
xtuc marked this conversation as resolved.
Show resolved Hide resolved
}

fn inner_module_run(&self, store: &mut Store, instance: Instance) -> Result<()> {
Expand Down Expand Up @@ -632,3 +654,47 @@ impl Run {
bail!("binfmt_misc is only available on linux.")
}
}

fn generate_coredump(
err: &anyhow::Error,
source_name: &str,
coredump_path: &PathBuf,
) -> Result<()> {
let err = err
.downcast_ref::<wasmer::RuntimeError>()
.ok_or_else(|| anyhow!("no runtime error found to generate coredump with"))?;

let mut coredump_builder =
wasm_coredump_builder::CoredumpBuilder::new().executable_name(source_name);

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

for frame in err.trace() {
let coredump_frame = wasm_coredump_builder::FrameBuilder::new()
.codeoffset(frame.func_offset() as u32)
.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.display()
))?;
f.write_all(&coredump).with_context(|| {
format!(
"failed to write coredump file at `{}`",
coredump_path.display()
)
})?;

Ok(())
}