diff --git a/Cargo.lock b/Cargo.lock
index 5136a372a4f..6d4a57595b5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4705,6 +4705,33 @@ dependencies = [
"quote",
]
+[[package]]
+name = "wasm-coredump-builder"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "158180f35c9ba89a3e7763f20be93e77d5e41535c18e22c85d6dd5b5bce18108"
+dependencies = [
+ "wasm-coredump-encoder",
+ "wasm-coredump-types",
+ "wasm-encoder 0.23.0",
+]
+
+[[package]]
+name = "wasm-coredump-encoder"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0c99cdf3a88363570f1027e2f337de6647cac9fed5d474f86103d7c45c8700"
+dependencies = [
+ "leb128",
+ "wasm-coredump-types",
+]
+
+[[package]]
+name = "wasm-coredump-types"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10e35729a021e44c20511e23ac2b215df05da243bdc4bad336fd3686552539fc"
+
[[package]]
name = "wasm-encoder"
version = "0.4.1"
@@ -4723,6 +4750,15 @@ dependencies = [
"leb128",
]
+[[package]]
+name = "wasm-encoder"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c3e4bc09095436c8e7584d86d33e6c3ee67045af8fb262cbb9cc321de553428"
+dependencies = [
+ "leb128",
+]
+
[[package]]
name = "wasm-smith"
version = "0.4.5"
@@ -4908,6 +4944,7 @@ dependencies = [
"unix_mode",
"url",
"walkdir",
+ "wasm-coredump-builder",
"wasmer",
"wasmer-cache",
"wasmer-compiler",
diff --git a/docs/en/examples-coredump.md b/docs/en/examples-coredump.md
new file mode 100644
index 00000000000..19a7754b101
--- /dev/null
+++ b/docs/en/examples-coredump.md
@@ -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
+ ```
+
+
+ foo.rs
+
+ 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);
+ }
+
+
+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
diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml
index d60553d1ef3..786d20e3610 100644
--- a/lib/cli/Cargo.toml
+++ b/lib/cli/Cargo.toml
@@ -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" ] }
diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs
index b80e920f8eb..e3c1c416969 100644
--- a/lib/cli/src/commands/run.rs
+++ b/lib/cli/src/commands/run.rs
@@ -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")]
@@ -89,6 +91,10 @@ pub struct RunWithoutFile {
#[clap(long = "verbose")]
pub(crate) verbose: Option,
+ /// Enable coredump generation after a WebAssembly trap.
+ #[clap(name = "COREDUMP PATH", long = "coredump-on-trap", parse(from_os_str))]
+ coredump_on_trap: Option,
+
/// Application arguments
#[clap(value_name = "ARGS")]
pub(crate) args: Vec,
@@ -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(),
@@ -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
+ }
}
fn inner_module_run(&self, store: &mut Store, instance: Instance) -> Result<()> {
@@ -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::()
+ .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(())
+}