diff --git a/crates/fuzzing/src/oracles/stacks.rs b/crates/fuzzing/src/oracles/stacks.rs index f397614952b3..3216ed10c802 100644 --- a/crates/fuzzing/src/oracles/stacks.rs +++ b/crates/fuzzing/src/oracles/stacks.rs @@ -73,7 +73,7 @@ pub fn check_stacks(stacks: Stacks) -> usize { .get_memory(&mut store, "memory") .expect("should have `memory` export"); - let host_trace = trap.downcast_ref::().unwrap().frames(); + let host_trace = trap.downcast_ref::().unwrap().frames(); let trap = trap.downcast_ref::().unwrap(); max_stack_depth = max_stack_depth.max(host_trace.len()); assert_stack_matches(&mut store, memory, ptr, len, host_trace, *trap); diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/trap.rs index 5d3b832dee6d..7840726d6a36 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/trap.rs @@ -71,23 +71,15 @@ pub enum Trap { OutOfFuel, } -#[derive(Debug)] -pub(crate) struct TrapBacktrace { - wasm_trace: Vec, - #[allow(dead_code)] - runtime_trace: wasmtime_runtime::Backtrace, - hint_wasm_backtrace_details_env: bool, -} - impl Trap { // Same safety requirements and caveats as // `wasmtime_runtime::raise_user_trap`. pub(crate) unsafe fn raise(error: anyhow::Error) -> ! { - let needs_backtrace = error.downcast_ref::().is_none(); + let needs_backtrace = error.downcast_ref::().is_none(); wasmtime_runtime::raise_user_trap(error, needs_backtrace) } - #[cold] // see Trap::new + #[cold] // traps are exceptional, this helps move handling off the main path pub(crate) fn from_runtime_box( store: &StoreOpaque, runtime_trap: Box, @@ -127,11 +119,11 @@ impl Trap { }; match backtrace { Some(bt) => { - let bt = TrapBacktrace::new(store, bt, pc); + let bt = WasmBacktrace::new(store, bt, pc); if bt.wasm_trace.is_empty() { error } else { - error.context(BacktraceContext(bt)) + error.context(bt) } } None => error, @@ -182,8 +174,17 @@ impl fmt::Display for Trap { impl std::error::Error for Trap {} -impl TrapBacktrace { - pub fn new( +/// todo +#[derive(Debug)] +pub struct WasmBacktrace { + wasm_trace: Vec, + #[allow(dead_code)] + runtime_trace: wasmtime_runtime::Backtrace, + hint_wasm_backtrace_details_env: bool, +} + +impl WasmBacktrace { + fn new( store: &StoreOpaque, runtime_trace: wasmtime_runtime::Backtrace, trap_pc: Option, @@ -258,11 +259,17 @@ impl TrapBacktrace { hint_wasm_backtrace_details_env, } } + + /// Returns a list of function frames in WebAssembly this backtrace + /// represents. + pub fn frames(&self) -> &[FrameInfo] { + self.wasm_trace.as_slice() + } } -impl fmt::Display for TrapBacktrace { +impl fmt::Display for WasmBacktrace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "wasm backtrace:")?; + writeln!(f, "error while executing at wasm backtrace:")?; let mut needs_newline = false; for (i, frame) in self.wasm_trace.iter().enumerate() { @@ -317,25 +324,6 @@ impl fmt::Display for TrapBacktrace { } } -/// Describes the context (backtrace) at which a user's error terminated (trapped) -/// WebAssembly execution -#[derive(Debug)] -pub struct BacktraceContext(TrapBacktrace); - -impl fmt::Display for BacktraceContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "error while executing at {}", self.0) - } -} - -impl BacktraceContext { - /// Returns a list of function frames in WebAssembly code that led to this - /// trap happening. - pub fn frames(&self) -> &[FrameInfo] { - self.0.wasm_trace.as_slice() - } -} - /// Description of a frame in a backtrace for a [`Trap`] or [`BacktraceContext`]. /// /// Whenever a WebAssembly trap occurs an instance of [`Trap`] is created. Each diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 4c72f5f2304e..9f2654ba7482 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -2,7 +2,7 @@ use super::REALLOC_AND_FREE; use anyhow::Result; use std::ops::Deref; use wasmtime::component::*; -use wasmtime::{BacktraceContext, Store, StoreContextMut}; +use wasmtime::{Store, StoreContextMut, WasmBacktrace}; #[test] fn can_compile() -> Result<()> { @@ -262,7 +262,7 @@ fn attempt_to_leave_during_malloc() -> Result<()> { "bad trap: {trap:?}", ); - let trace = trap.downcast_ref::().unwrap().frames(); + let trace = trap.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 4); // This was our entry point... diff --git a/tests/all/traps.rs b/tests/all/traps.rs index b03328e8c9d5..c01ddb88e633 100644 --- a/tests/all/traps.rs +++ b/tests/all/traps.rs @@ -24,8 +24,8 @@ fn test_trap_return() -> Result<()> { assert!(format!("{e:?}").contains("test 123")); assert!( - e.downcast_ref::().is_some(), - "error should contain a BacktraceContext" + e.downcast_ref::().is_some(), + "error should contain a WasmBacktrace" ); Ok(()) @@ -55,7 +55,7 @@ fn test_anyhow_error_return() -> Result<()> { assert!(format!("{:?}", e).contains("Caused by:\n test 1234")); assert!(e.downcast_ref::().is_none()); - assert!(e.downcast_ref::().is_some()); + assert!(e.downcast_ref::().is_some()); Ok(()) } @@ -101,8 +101,8 @@ fn test_trap_return_downcast() -> Result<()> { e.downcast_ref::() .expect("error downcasts to MyTrap"); let bt = e - .downcast_ref::() - .expect("error downcasts to BacktraceContext"); + .downcast_ref::() + .expect("error downcasts to WasmBacktrace"); assert_eq!(bt.frames().len(), 1); println!("{:?}", bt); @@ -125,7 +125,7 @@ fn test_trap_trace() -> Result<()> { let e = run_func.call(&mut store, ()).unwrap_err(); - let trace = e.downcast_ref::().unwrap().frames(); + let trace = e.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 2); assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); assert_eq!(trace[0].func_index(), 1); @@ -198,7 +198,7 @@ fn test_trap_through_host() -> Result<()> { )?; let a = instance.get_typed_func::<(), (), _>(&mut store, "a")?; let err = a.call(&mut store, ()).unwrap_err(); - let trace = err.downcast_ref::().unwrap().frames(); + let trace = err.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 3); assert_eq!(trace[0].func_name(), Some("c")); assert_eq!(trace[1].func_name(), Some("b")); @@ -225,7 +225,7 @@ fn test_trap_backtrace_disabled() -> Result<()> { let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?; let e = run_func.call(&mut store, ()).unwrap_err(); - assert!(e.downcast_ref::().is_none()); + assert!(e.downcast_ref::().is_none()); Ok(()) } @@ -249,7 +249,7 @@ fn test_trap_trace_cb() -> Result<()> { let e = run_func.call(&mut store, ()).unwrap_err(); - let trace = e.downcast_ref::().unwrap().frames(); + let trace = e.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 2); assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); assert_eq!(trace[0].func_index(), 2); @@ -275,7 +275,7 @@ fn test_trap_stack_overflow() -> Result<()> { let e = run_func.call(&mut store, ()).unwrap_err(); - let trace = e.downcast_ref::().unwrap().frames(); + let trace = e.downcast_ref::().unwrap().frames(); assert!(trace.len() >= 32); for i in 0..trace.len() { assert_eq!(trace[i].module_name().unwrap(), "rec_mod"); @@ -470,7 +470,7 @@ fn rust_catch_panic_import() -> Result<()> { let instance = Instance::new(&mut store, &module, &[panic.into(), catch_panic.into()])?; let run = instance.get_typed_func::<(), (), _>(&mut store, "run")?; let trap = run.call(&mut store, ()).unwrap_err(); - let trace = trap.downcast_ref::().unwrap().frames(); + let trace = trap.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 1); assert_eq!(trace[0].func_index(), 3); assert_eq!(num_panics.load(std::sync::atomic::Ordering::SeqCst), 2); @@ -625,7 +625,7 @@ fn present_after_module_drop() -> Result<()> { fn assert_trap(t: Error) { println!("{:?}", t); - let trace = t.downcast_ref::().unwrap().frames(); + let trace = t.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 1); assert_eq!(trace[0].func_index(), 0); } @@ -718,7 +718,7 @@ fn parse_dwarf_info() -> Result<()> { let trap = run.call(&mut store, &[], &mut []).unwrap_err(); let mut found = false; - let frames = trap.downcast_ref::().unwrap().frames(); + let frames = trap.downcast_ref::().unwrap().frames(); for frame in frames { for symbol in frame.symbols() { if let Some(file) = symbol.file() { @@ -847,7 +847,7 @@ fn traps_without_address_map() -> Result<()> { let e = run_func.call(&mut store, ()).unwrap_err(); - let trace = e.downcast_ref::().unwrap().frames(); + let trace = e.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 2); assert_eq!(trace[0].func_name(), Some("hello")); assert_eq!(trace[0].func_index(), 1); @@ -900,7 +900,7 @@ fn catch_trap_calling_across_stores() -> Result<()> { "trap should contain 'unreachable', got: {trap:?}" ); - let trace = trap.downcast_ref::().unwrap().frames(); + let trace = trap.downcast_ref::().unwrap().frames(); assert_eq!(trace.len(), 1); assert_eq!(trace[0].func_name(), Some("trap")); @@ -1010,7 +1010,7 @@ async fn async_then_sync_trap() -> Result<()> { .unwrap(); let trap = a.call_async(&mut async_store, ()).await.unwrap_err(); - let trace = trap.downcast_ref::().unwrap().frames(); + let trace = trap.downcast_ref::().unwrap().frames(); // We don't support cross-store or cross-engine symbolication currently, so // the other frames are ignored. assert_eq!(trace.len(), 1); @@ -1091,7 +1091,7 @@ async fn sync_then_async_trap() -> Result<()> { .unwrap(); let trap = a.call(&mut sync_store, ()).unwrap_err(); - let trace = trap.downcast_ref::().unwrap().frames(); + let trace = trap.downcast_ref::().unwrap().frames(); // We don't support cross-store or cross-engine symbolication currently, so // the other frames are ignored. assert_eq!(trace.len(), 1);