Improve trap error messages (#831)
* Improve trap error messages The new trap error message for the issue #828 looks like: ``` thread 'main' panicked at 'a', /proc/self/fd/11:1:13 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. Error: failed to run main module `test.wasm` Caused by: 0: failed to invoke `_start` 1: wasm trap: unreachable, source location: @6cea wasm backtrace: 0: __rust_start_panic 1: rust_panic 2: std::panicking::rust_panic_with_hook::h57f0cff11449798f 3: std::panicking::begin_panic::hd620695467c5dd1f 4: test::main::ha54db001eabbde1b 5: std::rt::lang_start::{{closure}}::h5acfb82693695869 6: std::sys_common::backtrace::__rust_begin_short_backtrace::h39e8b9420da241f9 7: std::panicking::try::do_call::hb7ebfcd70d5f703e 8: __rust_maybe_catch_panic 9: std::rt::lang_start_internal::hd5f64f52a5c5315c 10: std::rt::lang_start::h2a51d79994dd0c4b 11: __original_main 12: _start ``` Closes #828 * Tidy up the style of the traps tests * Add some tests and module names
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2061,6 +2061,7 @@ dependencies = [
|
||||
"pretty_env_logger",
|
||||
"rayon",
|
||||
"region",
|
||||
"rustc-demangle",
|
||||
"target-lexicon",
|
||||
"wasi-common",
|
||||
"wasmparser 0.47.0",
|
||||
|
||||
@@ -19,6 +19,7 @@ region = "2.0.0"
|
||||
libc = "0.2"
|
||||
cfg-if = "0.1.9"
|
||||
backtrace = "0.3.42"
|
||||
rustc-demangle = "0.1.16"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.3.7"
|
||||
|
||||
@@ -78,7 +78,25 @@ impl fmt::Debug for Trap {
|
||||
|
||||
impl fmt::Display for Trap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.inner.message.fmt(f)
|
||||
write!(f, "{}", self.inner.message)?;
|
||||
let trace = self.trace();
|
||||
if trace.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
writeln!(f, "\nwasm backtrace:")?;
|
||||
for (i, frame) in self.trace().iter().enumerate() {
|
||||
let name = frame.module_name().unwrap_or("<unknown>");
|
||||
write!(f, " {}: {}!", i, name)?;
|
||||
match frame.func_name() {
|
||||
Some(name) => match rustc_demangle::try_demangle(name) {
|
||||
Ok(name) => write!(f, "{}", name)?,
|
||||
Err(_) => write!(f, "{}", name)?,
|
||||
},
|
||||
None => write!(f, "<wasm function {}>", frame.func_index)?,
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use anyhow::Result;
|
||||
use std::rc::Rc;
|
||||
use wasmtime::*;
|
||||
use wat::parse_str;
|
||||
|
||||
#[test]
|
||||
fn test_trap_return() -> Result<(), String> {
|
||||
fn test_trap_return() -> Result<()> {
|
||||
struct HelloCallback;
|
||||
|
||||
impl Callable for HelloCallback {
|
||||
@@ -13,24 +13,20 @@ fn test_trap_return() -> Result<(), String> {
|
||||
}
|
||||
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module
|
||||
(func $hello (import "" "hello"))
|
||||
(func (export "run") (call $hello))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
)?;
|
||||
|
||||
let module =
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||
let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
|
||||
|
||||
let imports = vec![hello_func.into()];
|
||||
let instance = Instance::new(&module, &imports)
|
||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
||||
let instance = Instance::new(&module, &[hello_func.into()])?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
@@ -43,22 +39,19 @@ fn test_trap_return() -> Result<(), String> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_trace() -> Result<(), String> {
|
||||
fn test_trap_trace() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $hello_mod
|
||||
(func (export "run") (call $hello))
|
||||
(func $hello (unreachable))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
)?;
|
||||
|
||||
let module =
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
||||
let instance = Instance::new(&module, &[])
|
||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
@@ -79,7 +72,7 @@ fn test_trap_trace() -> Result<(), String> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_trace_cb() -> Result<(), String> {
|
||||
fn test_trap_trace_cb() -> Result<()> {
|
||||
struct ThrowCallback;
|
||||
|
||||
impl Callable for ThrowCallback {
|
||||
@@ -89,7 +82,7 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
||||
}
|
||||
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $hello_mod
|
||||
(import "" "throw" (func $throw))
|
||||
@@ -97,16 +90,13 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
||||
(func $hello (call $throw))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
)?;
|
||||
|
||||
let fn_type = FuncType::new(Box::new([]), Box::new([]));
|
||||
let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback));
|
||||
|
||||
let module =
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
||||
let instance = Instance::new(&module, &[fn_func.into()])
|
||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[fn_func.into()])?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
@@ -125,21 +115,18 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_stack_overflow() -> Result<(), String> {
|
||||
fn test_trap_stack_overflow() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = parse_str(
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $rec_mod
|
||||
(func $run (export "run") (call $run))
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
||||
)?;
|
||||
|
||||
let module =
|
||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
||||
let instance = Instance::new(&module, &[])
|
||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
@@ -151,9 +138,94 @@ fn test_trap_stack_overflow() -> Result<(), String> {
|
||||
for i in 0..trace.len() {
|
||||
assert_eq!(trace[i].module_name().unwrap(), "rec_mod");
|
||||
assert_eq!(trace[i].func_index(), 0);
|
||||
assert_eq!(trace[1].func_name(), Some("run"));
|
||||
assert_eq!(trace[i].func_name(), Some("run"));
|
||||
}
|
||||
assert!(e.message().contains("call stack exhausted"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trap_display_pretty() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $m
|
||||
(func $die unreachable)
|
||||
(func call $die)
|
||||
(func $foo call 1)
|
||||
(func (export "bar") call $foo)
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
let run_func = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
|
||||
let e = run_func.call(&[]).err().expect("error calling function");
|
||||
assert_eq!(
|
||||
e.to_string(),
|
||||
"\
|
||||
wasm trap: unreachable, source location: @0023
|
||||
wasm backtrace:
|
||||
0: m!die
|
||||
1: m!<wasm function 1>
|
||||
2: m!foo
|
||||
3: m!<wasm function 3>
|
||||
"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trap_display_multi_module() -> Result<()> {
|
||||
let store = Store::default();
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $a
|
||||
(func $die unreachable)
|
||||
(func call $die)
|
||||
(func $foo call 1)
|
||||
(func (export "bar") call $foo)
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[])?;
|
||||
let bar = instance.exports()[0].clone();
|
||||
|
||||
let binary = wat::parse_str(
|
||||
r#"
|
||||
(module $b
|
||||
(import "" "" (func $bar))
|
||||
(func $middle call $bar)
|
||||
(func (export "bar2") call $middle)
|
||||
)
|
||||
"#,
|
||||
)?;
|
||||
let module = Module::new(&store, &binary)?;
|
||||
let instance = Instance::new(&module, &[bar])?;
|
||||
let bar2 = instance.exports()[0]
|
||||
.func()
|
||||
.expect("expected function export");
|
||||
|
||||
let e = bar2.call(&[]).err().expect("error calling function");
|
||||
assert_eq!(
|
||||
e.to_string(),
|
||||
"\
|
||||
wasm trap: unreachable, source location: @0023
|
||||
wasm backtrace:
|
||||
0: a!die
|
||||
1: a!<wasm function 1>
|
||||
2: a!foo
|
||||
3: a!<wasm function 3>
|
||||
4: b!middle
|
||||
5: b!<wasm function 2>
|
||||
"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -147,8 +147,8 @@ impl ModuleData {
|
||||
.into_iter()
|
||||
.map(|rv| rv.into())
|
||||
.collect::<Vec<_>>();
|
||||
let wasm_results = match f.call(&wasm_args) {
|
||||
Ok(values) => values
|
||||
let wasm_results = f
|
||||
.call(&wasm_args)?
|
||||
.to_vec()
|
||||
.into_iter()
|
||||
.map(|v: wasmtime::Val| match v {
|
||||
@@ -159,9 +159,7 @@ impl ModuleData {
|
||||
wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()),
|
||||
_ => panic!("unsupported value {:?}", v),
|
||||
})
|
||||
.collect::<Vec<RuntimeValue>>(),
|
||||
Err(trap) => bail!("trapped: {:?}", trap),
|
||||
};
|
||||
.collect::<Vec<RuntimeValue>>();
|
||||
translate_outgoing(&mut cx, &outgoing, &wasm_results)
|
||||
}
|
||||
|
||||
@@ -333,10 +331,7 @@ impl TranslateContext for InstanceTranslateContext {
|
||||
.ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))?
|
||||
.clone();
|
||||
let alloc_args = vec![wasmtime::Val::I32(len)];
|
||||
let results = match alloc.call(&alloc_args) {
|
||||
Ok(values) => values,
|
||||
Err(trap) => bail!("trapped: {:?}", trap),
|
||||
};
|
||||
let results = alloc.call(&alloc_args)?;
|
||||
if results.len() != 1 {
|
||||
bail!("allocator function wrong number of results");
|
||||
}
|
||||
|
||||
@@ -71,13 +71,10 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
|
||||
.context("expected a _start export")?
|
||||
.clone();
|
||||
|
||||
if let Err(trap) = export
|
||||
export
|
||||
.func()
|
||||
.context("expected export to be a func")?
|
||||
.call(&[])
|
||||
{
|
||||
bail!("trapped: {:?}", trap);
|
||||
}
|
||||
.call(&[])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user