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:

```
wasmer --coredump-on-trap=/path/to/coredump/file module.wasm
```

Refs #3578
  • Loading branch information
xtuc committed Feb 24, 2023
1 parent 4d8163d commit 38f573f
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
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.

1 change: 1 addition & 0 deletions lib/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,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
60 changes: 58 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(long = "coredump-on-trap", value_name = "PATH")]
coredump_on_trap: Option<String>,

/// 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)))
}
} else {
Err(err)
}
} else {
invoke_res
}
}

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

fn generate_coredump(err: &anyhow::Error, source_name: &str, coredump_path: &str) -> 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))?;
f.write_all(&coredump)
.with_context(|| format!("failed to write coredump file at `{}`", coredump_path))?;

Ok(())
}

0 comments on commit 38f573f

Please sign in to comment.