diff --git a/Cargo.lock b/Cargo.lock
index d9aa04d192f6..57956a4ce4b9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1418,6 +1418,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "hermit-abi"
version = "0.3.0"
@@ -1875,11 +1884,11 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.13.1"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
- "hermit-abi 0.1.19",
+ "hermit-abi 0.2.6",
"libc",
]
@@ -3268,6 +3277,33 @@ version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
+[[package]]
+name = "wasm-coredump-builder"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cbb06cb8a29340498b1e3a8f1ea1c7ef577594960da204c392c794ee54757b9"
+dependencies = [
+ "wasm-coredump-encoder",
+ "wasm-coredump-types",
+ "wasm-encoder",
+]
+
+[[package]]
+name = "wasm-coredump-encoder"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a07032be7b990939441f09a68232fe5a7c75056438b9721226dc56985e48ccdd"
+dependencies = [
+ "leb128",
+ "wasm-coredump-types",
+]
+
+[[package]]
+name = "wasm-coredump-types"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a70ce904d36ee12c9953398359e8765b8519bf26730c450406ce07098cf58318"
+
[[package]]
name = "wasm-encoder"
version = "0.23.0"
@@ -3506,6 +3542,7 @@ dependencies = [
"tempfile",
"test-programs",
"tokio",
+ "wasm-coredump-builder",
"wasmparser",
"wasmtime",
"wasmtime-cache",
diff --git a/Cargo.toml b/Cargo.toml
index f052cbe1d628..83b2809f8cc8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"] }
diff --git a/docs/examples-coredump.md b/docs/examples-coredump.md
new file mode 100644
index 000000000000..6a78dd5176f2
--- /dev/null
+++ b/docs/examples-coredump.md
@@ -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
+ ```
+
+
+ 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 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
diff --git a/src/commands/run.rs b/src/commands/run.rs
index 4f5751c3befe..1a242d316be5 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -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};
@@ -151,6 +153,10 @@ pub struct RunCommand {
)]
wasm_timeout: Option,
+ /// Enable coredump generation after a WebAssembly trap.
+ #[clap(long = "coredump-on-trap", value_name = "PATH")]
+ coredump_on_trap: Option,
+
// NOTE: this must come last for trailing varargs
/// The arguments to pass to the module
#[clap(value_name = "ARGS")]
@@ -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();
@@ -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::()
+ .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 \
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index e4126cf0b1b7..6ad92f85f227 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -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 "
+criteria = "safe-to-deploy"
+version = "0.1.10"
+
+[[audits.wasm-coredump-encoder]]
+who = "Sven Sauleau "
+criteria = "safe-to-deploy"
+version = "0.1.10"
+
+[[audits.wasm-coredump-types]]
+who = "Sven Sauleau "
+criteria = "safe-to-deploy"
+version = "0.1.10"
+
[[audits.wasm-encoder]]
who = "Alex Crichton "
criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 5d7eac22f243..e11a1d35146a 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -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 "
+criteria = "safe-to-deploy"
+delta = "0.1.19 -> 0.2.6"
+
[[audits.mozilla.audits.indexmap]]
who = "Mike Hommey "
criteria = "safe-to-deploy"
@@ -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 "
+criteria = "safe-to-deploy"
+delta = "1.13.1 -> 1.14.0"
+
+[[audits.mozilla.audits.num_cpus]]
+who = "Mike Hommey "
+criteria = "safe-to-deploy"
+delta = "1.14.0 -> 1.15.0"
+
[[audits.mozilla.audits.once_cell]]
who = "Mike Hommey "
criteria = "safe-to-deploy"