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",
|
"pretty_env_logger",
|
||||||
"rayon",
|
"rayon",
|
||||||
"region",
|
"region",
|
||||||
|
"rustc-demangle",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"wasi-common",
|
"wasi-common",
|
||||||
"wasmparser 0.47.0",
|
"wasmparser 0.47.0",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ region = "2.0.0"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
cfg-if = "0.1.9"
|
cfg-if = "0.1.9"
|
||||||
backtrace = "0.3.42"
|
backtrace = "0.3.42"
|
||||||
|
rustc-demangle = "0.1.16"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.3.7"
|
winapi = "0.3.7"
|
||||||
|
|||||||
@@ -78,7 +78,25 @@ impl fmt::Debug for Trap {
|
|||||||
|
|
||||||
impl fmt::Display for Trap {
|
impl fmt::Display for Trap {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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 std::rc::Rc;
|
||||||
use wasmtime::*;
|
use wasmtime::*;
|
||||||
use wat::parse_str;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trap_return() -> Result<(), String> {
|
fn test_trap_return() -> Result<()> {
|
||||||
struct HelloCallback;
|
struct HelloCallback;
|
||||||
|
|
||||||
impl Callable for HelloCallback {
|
impl Callable for HelloCallback {
|
||||||
@@ -13,24 +13,20 @@ fn test_trap_return() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = parse_str(
|
let binary = wat::parse_str(
|
||||||
r#"
|
r#"
|
||||||
(module
|
(module
|
||||||
(func $hello (import "" "hello"))
|
(func $hello (import "" "hello"))
|
||||||
(func (export "run") (call $hello))
|
(func (export "run") (call $hello))
|
||||||
)
|
)
|
||||||
"#,
|
"#,
|
||||||
)
|
)?;
|
||||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
|
||||||
|
|
||||||
let module =
|
let module = Module::new(&store, &binary)?;
|
||||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
|
||||||
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
let hello_type = FuncType::new(Box::new([]), Box::new([]));
|
||||||
let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
|
let hello_func = Func::new(&store, hello_type, Rc::new(HelloCallback));
|
||||||
|
|
||||||
let imports = vec![hello_func.into()];
|
let instance = Instance::new(&module, &[hello_func.into()])?;
|
||||||
let instance = Instance::new(&module, &imports)
|
|
||||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
|
||||||
let run_func = instance.exports()[0]
|
let run_func = instance.exports()[0]
|
||||||
.func()
|
.func()
|
||||||
.expect("expected function export");
|
.expect("expected function export");
|
||||||
@@ -43,22 +39,19 @@ fn test_trap_return() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trap_trace() -> Result<(), String> {
|
fn test_trap_trace() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = parse_str(
|
let binary = wat::parse_str(
|
||||||
r#"
|
r#"
|
||||||
(module $hello_mod
|
(module $hello_mod
|
||||||
(func (export "run") (call $hello))
|
(func (export "run") (call $hello))
|
||||||
(func $hello (unreachable))
|
(func $hello (unreachable))
|
||||||
)
|
)
|
||||||
"#,
|
"#,
|
||||||
)
|
)?;
|
||||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
|
||||||
|
|
||||||
let module =
|
let module = Module::new(&store, &binary)?;
|
||||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
let instance = Instance::new(&module, &[])?;
|
||||||
let instance = Instance::new(&module, &[])
|
|
||||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
|
||||||
let run_func = instance.exports()[0]
|
let run_func = instance.exports()[0]
|
||||||
.func()
|
.func()
|
||||||
.expect("expected function export");
|
.expect("expected function export");
|
||||||
@@ -79,7 +72,7 @@ fn test_trap_trace() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trap_trace_cb() -> Result<(), String> {
|
fn test_trap_trace_cb() -> Result<()> {
|
||||||
struct ThrowCallback;
|
struct ThrowCallback;
|
||||||
|
|
||||||
impl Callable for ThrowCallback {
|
impl Callable for ThrowCallback {
|
||||||
@@ -89,7 +82,7 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = parse_str(
|
let binary = wat::parse_str(
|
||||||
r#"
|
r#"
|
||||||
(module $hello_mod
|
(module $hello_mod
|
||||||
(import "" "throw" (func $throw))
|
(import "" "throw" (func $throw))
|
||||||
@@ -97,16 +90,13 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
|||||||
(func $hello (call $throw))
|
(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_type = FuncType::new(Box::new([]), Box::new([]));
|
||||||
let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback));
|
let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback));
|
||||||
|
|
||||||
let module =
|
let module = Module::new(&store, &binary)?;
|
||||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
let instance = Instance::new(&module, &[fn_func.into()])?;
|
||||||
let instance = Instance::new(&module, &[fn_func.into()])
|
|
||||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
|
||||||
let run_func = instance.exports()[0]
|
let run_func = instance.exports()[0]
|
||||||
.func()
|
.func()
|
||||||
.expect("expected function export");
|
.expect("expected function export");
|
||||||
@@ -125,21 +115,18 @@ fn test_trap_trace_cb() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trap_stack_overflow() -> Result<(), String> {
|
fn test_trap_stack_overflow() -> Result<()> {
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
let binary = parse_str(
|
let binary = wat::parse_str(
|
||||||
r#"
|
r#"
|
||||||
(module $rec_mod
|
(module $rec_mod
|
||||||
(func $run (export "run") (call $run))
|
(func $run (export "run") (call $run))
|
||||||
)
|
)
|
||||||
"#,
|
"#,
|
||||||
)
|
)?;
|
||||||
.map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?;
|
|
||||||
|
|
||||||
let module =
|
let module = Module::new(&store, &binary)?;
|
||||||
Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?;
|
let instance = Instance::new(&module, &[])?;
|
||||||
let instance = Instance::new(&module, &[])
|
|
||||||
.map_err(|e| format!("failed to instantiate module: {:?}", e))?;
|
|
||||||
let run_func = instance.exports()[0]
|
let run_func = instance.exports()[0]
|
||||||
.func()
|
.func()
|
||||||
.expect("expected function export");
|
.expect("expected function export");
|
||||||
@@ -151,9 +138,94 @@ fn test_trap_stack_overflow() -> Result<(), String> {
|
|||||||
for i in 0..trace.len() {
|
for i in 0..trace.len() {
|
||||||
assert_eq!(trace[i].module_name().unwrap(), "rec_mod");
|
assert_eq!(trace[i].module_name().unwrap(), "rec_mod");
|
||||||
assert_eq!(trace[i].func_index(), 0);
|
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"));
|
assert!(e.message().contains("call stack exhausted"));
|
||||||
|
|
||||||
Ok(())
|
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,21 +147,19 @@ impl ModuleData {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|rv| rv.into())
|
.map(|rv| rv.into())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let wasm_results = match f.call(&wasm_args) {
|
let wasm_results = f
|
||||||
Ok(values) => values
|
.call(&wasm_args)?
|
||||||
.to_vec()
|
.to_vec()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v: wasmtime::Val| match v {
|
.map(|v: wasmtime::Val| match v {
|
||||||
wasmtime::Val::I32(i) => RuntimeValue::I32(i),
|
wasmtime::Val::I32(i) => RuntimeValue::I32(i),
|
||||||
wasmtime::Val::I64(i) => RuntimeValue::I64(i),
|
wasmtime::Val::I64(i) => RuntimeValue::I64(i),
|
||||||
wasmtime::Val::F32(i) => RuntimeValue::F32(i),
|
wasmtime::Val::F32(i) => RuntimeValue::F32(i),
|
||||||
wasmtime::Val::F64(i) => RuntimeValue::F64(i),
|
wasmtime::Val::F64(i) => RuntimeValue::F64(i),
|
||||||
wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()),
|
wasmtime::Val::V128(i) => RuntimeValue::V128(i.to_le_bytes()),
|
||||||
_ => panic!("unsupported value {:?}", v),
|
_ => panic!("unsupported value {:?}", v),
|
||||||
})
|
})
|
||||||
.collect::<Vec<RuntimeValue>>(),
|
.collect::<Vec<RuntimeValue>>();
|
||||||
Err(trap) => bail!("trapped: {:?}", trap),
|
|
||||||
};
|
|
||||||
translate_outgoing(&mut cx, &outgoing, &wasm_results)
|
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))?
|
.ok_or_else(|| format_err!("`{}` is not a (alloc) function", alloc_func_name))?
|
||||||
.clone();
|
.clone();
|
||||||
let alloc_args = vec![wasmtime::Val::I32(len)];
|
let alloc_args = vec![wasmtime::Val::I32(len)];
|
||||||
let results = match alloc.call(&alloc_args) {
|
let results = alloc.call(&alloc_args)?;
|
||||||
Ok(values) => values,
|
|
||||||
Err(trap) => bail!("trapped: {:?}", trap),
|
|
||||||
};
|
|
||||||
if results.len() != 1 {
|
if results.len() != 1 {
|
||||||
bail!("allocator function wrong number of results");
|
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")?
|
.context("expected a _start export")?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
if let Err(trap) = export
|
export
|
||||||
.func()
|
.func()
|
||||||
.context("expected export to be a func")?
|
.context("expected export to be a func")?
|
||||||
.call(&[])
|
.call(&[])?;
|
||||||
{
|
|
||||||
bail!("trapped: {:?}", trap);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user