make backtrace collection a Config field rather than a cargo feature (#4183)
* sorta working in runtime * wasmtime-runtime: get rid of wasm-backtrace feature * wasmtime: factor to make backtraces recording optional. not configurable yet * get rid of wasm-backtrace features * trap tests: now a Trap optionally contains backtrace * eliminate wasm-backtrace feature * code review fixes * ci: no more wasm-backtrace feature * c_api: backtraces always enabled * config: unwind required by backtraces and ref types * plumbed * test that disabling backtraces works * code review comments * fuzzing generator: wasm_backtrace is a runtime config now * doc fix
This commit is contained in:
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
@@ -136,9 +136,8 @@ jobs:
|
||||
- run: cargo check -p wasmtime --no-default-features --features async
|
||||
- run: cargo check -p wasmtime --no-default-features --features pooling-allocator
|
||||
- run: cargo check -p wasmtime --no-default-features --features cranelift
|
||||
- run: cargo check -p wasmtime --no-default-features --features wasm-backtrace
|
||||
- run: cargo check -p wasmtime --no-default-features --features component-model
|
||||
- run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache,wasm-backtrace
|
||||
- run: cargo check -p wasmtime --no-default-features --features cranelift,wat,async,cache
|
||||
- run: cargo check --features component-model
|
||||
|
||||
# Check that benchmarks of the cranelift project build
|
||||
|
||||
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -1871,9 +1871,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.10.0"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
|
||||
@@ -98,7 +98,6 @@ default = [
|
||||
"wasi-nn",
|
||||
"pooling-allocator",
|
||||
"memory-init-cow",
|
||||
"wasm-backtrace",
|
||||
]
|
||||
jitdump = ["wasmtime/jitdump"]
|
||||
vtune = ["wasmtime/vtune"]
|
||||
@@ -108,7 +107,6 @@ memory-init-cow = ["wasmtime/memory-init-cow", "wasmtime-cli-flags/memory-init-c
|
||||
pooling-allocator = ["wasmtime/pooling-allocator", "wasmtime-cli-flags/pooling-allocator"]
|
||||
all-arch = ["wasmtime/all-arch"]
|
||||
posix-signals-on-macos = ["wasmtime/posix-signals-on-macos"]
|
||||
wasm-backtrace = ["wasmtime/wasm-backtrace", "wasmtime-cli-flags/wasm-backtrace"]
|
||||
component-model = ["wasmtime/component-model", "wasmtime-wast/component-model", "wasmtime-cli-flags/component-model"]
|
||||
|
||||
# Stub feature that does nothing, for Cargo-features compatibility: the new
|
||||
|
||||
@@ -20,7 +20,7 @@ doctest = false
|
||||
env_logger = "0.9"
|
||||
anyhow = "1.0"
|
||||
once_cell = "1.3"
|
||||
wasmtime = { path = "../wasmtime", default-features = false, features = ['cranelift', 'wasm-backtrace'] }
|
||||
wasmtime = { path = "../wasmtime", default-features = false, features = ['cranelift'] }
|
||||
wasmtime-c-api-macros = { path = "macros" }
|
||||
|
||||
# Optional dependency for the `wat2wasm` API
|
||||
|
||||
@@ -64,7 +64,13 @@ pub extern "C" fn wasm_trap_message(trap: &wasm_trap_t, out: &mut wasm_message_t
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_trap_origin(raw: &wasm_trap_t) -> Option<Box<wasm_frame_t>> {
|
||||
if raw.trap.trace().len() > 0 {
|
||||
if raw
|
||||
.trap
|
||||
.trace()
|
||||
.expect("backtraces are always enabled")
|
||||
.len()
|
||||
> 0
|
||||
{
|
||||
Some(Box::new(wasm_frame_t {
|
||||
trap: raw.trap.clone(),
|
||||
idx: 0,
|
||||
@@ -78,7 +84,11 @@ pub extern "C" fn wasm_trap_origin(raw: &wasm_trap_t) -> Option<Box<wasm_frame_t
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_trap_trace(raw: &wasm_trap_t, out: &mut wasm_frame_vec_t) {
|
||||
let vec = (0..raw.trap.trace().len())
|
||||
let vec = (0..raw
|
||||
.trap
|
||||
.trace()
|
||||
.expect("backtraces are always enabled")
|
||||
.len())
|
||||
.map(|idx| {
|
||||
Some(Box::new(wasm_frame_t {
|
||||
trap: raw.trap.clone(),
|
||||
@@ -128,7 +138,7 @@ pub extern "C" fn wasmtime_trap_exit_status(raw: &wasm_trap_t, status: &mut i32)
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_frame_func_index(frame: &wasm_frame_t) -> u32 {
|
||||
frame.trap.trace()[frame.idx].func_index()
|
||||
frame.trap.trace().expect("backtraces are always enabled")[frame.idx].func_index()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -136,7 +146,7 @@ pub extern "C" fn wasmtime_frame_func_name(frame: &wasm_frame_t) -> Option<&wasm
|
||||
frame
|
||||
.func_name
|
||||
.get_or_init(|| {
|
||||
frame.trap.trace()[frame.idx]
|
||||
frame.trap.trace().expect("backtraces are always enabled")[frame.idx]
|
||||
.func_name()
|
||||
.map(|s| wasm_name_t::from(s.to_string().into_bytes()))
|
||||
})
|
||||
@@ -148,7 +158,7 @@ pub extern "C" fn wasmtime_frame_module_name(frame: &wasm_frame_t) -> Option<&wa
|
||||
frame
|
||||
.module_name
|
||||
.get_or_init(|| {
|
||||
frame.trap.trace()[frame.idx]
|
||||
frame.trap.trace().expect("backtraces are always enabled")[frame.idx]
|
||||
.module_name()
|
||||
.map(|s| wasm_name_t::from(s.to_string().into_bytes()))
|
||||
})
|
||||
@@ -157,7 +167,7 @@ pub extern "C" fn wasmtime_frame_module_name(frame: &wasm_frame_t) -> Option<&wa
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_frame_func_offset(frame: &wasm_frame_t) -> usize {
|
||||
frame.trap.trace()[frame.idx]
|
||||
frame.trap.trace().expect("backtraces are always enabled")[frame.idx]
|
||||
.func_offset()
|
||||
.unwrap_or(usize::MAX)
|
||||
}
|
||||
@@ -169,7 +179,7 @@ pub extern "C" fn wasm_frame_instance(_arg1: *const wasm_frame_t) -> *mut wasm_i
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wasm_frame_module_offset(frame: &wasm_frame_t) -> usize {
|
||||
frame.trap.trace()[frame.idx]
|
||||
frame.trap.trace().expect("backtraces are always enabled")[frame.idx]
|
||||
.module_offset()
|
||||
.unwrap_or(usize::MAX)
|
||||
}
|
||||
|
||||
@@ -25,5 +25,4 @@ default = [
|
||||
]
|
||||
pooling-allocator = []
|
||||
memory-init-cow = []
|
||||
wasm-backtrace = []
|
||||
component-model = []
|
||||
|
||||
@@ -346,9 +346,7 @@ impl CommonOptions {
|
||||
config.wasm_bulk_memory(enable);
|
||||
}
|
||||
if let Some(enable) = reference_types {
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
config.wasm_reference_types(enable);
|
||||
drop(enable); // suppress unused warnings
|
||||
}
|
||||
if let Some(enable) = multi_value {
|
||||
config.wasm_multi_value(enable);
|
||||
|
||||
@@ -213,6 +213,7 @@ pub struct WasmtimeConfig {
|
||||
codegen: CodegenSettings,
|
||||
padding_between_functions: Option<u16>,
|
||||
generate_address_map: bool,
|
||||
wasm_backtraces: bool,
|
||||
}
|
||||
|
||||
/// Configuration for linear memories in Wasmtime.
|
||||
@@ -387,6 +388,7 @@ impl Config {
|
||||
.wasm_multi_memory(self.module_config.config.max_memories > 1)
|
||||
.wasm_simd(self.module_config.config.simd_enabled)
|
||||
.wasm_memory64(self.module_config.config.memory64_enabled)
|
||||
.wasm_backtrace(self.wasmtime.wasm_backtraces)
|
||||
.cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans)
|
||||
.cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime())
|
||||
.consume_fuel(self.wasmtime.consume_fuel)
|
||||
|
||||
@@ -22,7 +22,7 @@ indexmap = "1.0.2"
|
||||
thiserror = "1.0.4"
|
||||
more-asserts = "0.2.1"
|
||||
cfg-if = "1.0"
|
||||
backtrace = { version = "0.3.61", optional = true }
|
||||
backtrace = { version = "0.3.61" }
|
||||
rand = "0.8.3"
|
||||
anyhow = "1.0.38"
|
||||
memfd = { version = "0.4.1", optional = true }
|
||||
@@ -44,7 +44,6 @@ maintenance = { status = "actively-developed" }
|
||||
|
||||
[features]
|
||||
memory-init-cow = ['memfd']
|
||||
wasm-backtrace = ["backtrace"]
|
||||
|
||||
async = ["wasmtime-fiber"]
|
||||
|
||||
|
||||
@@ -704,7 +704,6 @@ impl VMExternRefActivationsTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "wasm-backtrace"), allow(dead_code))]
|
||||
fn insert_precise_stack_root(
|
||||
precise_stack_roots: &mut HashSet<VMExternRefWithTraits>,
|
||||
root: NonNull<VMExternData>,
|
||||
@@ -867,7 +866,6 @@ impl<T> std::ops::DerefMut for DebugOnly<T> {
|
||||
///
|
||||
/// Additionally, you must have registered the stack maps for every Wasm module
|
||||
/// that has frames on the stack with the given `stack_maps_registry`.
|
||||
#[cfg_attr(not(feature = "wasm-backtrace"), allow(unused_mut, unused_variables))]
|
||||
pub unsafe fn gc(
|
||||
module_info_lookup: &dyn ModuleInfoLookup,
|
||||
externref_activations_table: &mut VMExternRefActivationsTable,
|
||||
@@ -895,7 +893,6 @@ pub unsafe fn gc(
|
||||
None => {
|
||||
if cfg!(debug_assertions) {
|
||||
// Assert that there aren't any Wasm frames on the stack.
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
backtrace::trace(|frame| {
|
||||
assert!(module_info_lookup.lookup(frame.ip() as usize).is_none());
|
||||
true
|
||||
@@ -937,7 +934,6 @@ pub unsafe fn gc(
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
backtrace::trace(|frame| {
|
||||
let pc = frame.ip() as usize;
|
||||
let sp = frame.sp() as usize;
|
||||
|
||||
@@ -507,7 +507,7 @@ pub unsafe extern "C" fn memory_atomic_notify(
|
||||
// just to be sure.
|
||||
let addr_to_check = addr.checked_add(4).unwrap();
|
||||
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
|
||||
Err(Trap::User(anyhow::anyhow!(
|
||||
Err(Trap::user(anyhow::anyhow!(
|
||||
"unimplemented: wasm atomics (fn memory_atomic_notify) unsupported",
|
||||
)))
|
||||
})
|
||||
@@ -534,7 +534,7 @@ pub unsafe extern "C" fn memory_atomic_wait32(
|
||||
// but we still double-check
|
||||
let addr_to_check = addr.checked_add(4).unwrap();
|
||||
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
|
||||
Err(Trap::User(anyhow::anyhow!(
|
||||
Err(Trap::user(anyhow::anyhow!(
|
||||
"unimplemented: wasm atomics (fn memory_atomic_wait32) unsupported",
|
||||
)))
|
||||
})
|
||||
@@ -561,7 +561,7 @@ pub unsafe extern "C" fn memory_atomic_wait64(
|
||||
// but we still double-check
|
||||
let addr_to_check = addr.checked_add(8).unwrap();
|
||||
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
|
||||
Err(Trap::User(anyhow::anyhow!(
|
||||
Err(Trap::user(anyhow::anyhow!(
|
||||
"unimplemented: wasm atomics (fn memory_atomic_wait64) unsupported",
|
||||
)))
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::sync::Once;
|
||||
use wasmtime_environ::TrapCode;
|
||||
|
||||
pub use self::tls::{tls_eager_initialize, TlsRestore};
|
||||
pub use backtrace::Backtrace;
|
||||
|
||||
#[link(name = "wasmtime-helpers")]
|
||||
extern "C" {
|
||||
@@ -112,14 +113,19 @@ pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
|
||||
#[derive(Debug)]
|
||||
pub enum Trap {
|
||||
/// A user-raised trap through `raise_user_trap`.
|
||||
User(Error),
|
||||
User {
|
||||
/// The user-provided error
|
||||
error: Error,
|
||||
/// Native stack backtrace at the time the trap occurred
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
|
||||
/// A trap raised from jit code
|
||||
Jit {
|
||||
/// The program counter in JIT code where this trap happened.
|
||||
pc: usize,
|
||||
/// Native stack backtrace at the time the trap occurred
|
||||
backtrace: Backtrace,
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
|
||||
/// A trap raised from a wasm libcall
|
||||
@@ -127,63 +133,53 @@ pub enum Trap {
|
||||
/// Code of the trap.
|
||||
trap_code: TrapCode,
|
||||
/// Native stack backtrace at the time the trap occurred
|
||||
backtrace: Backtrace,
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
|
||||
/// A trap indicating that the runtime was unable to allocate sufficient memory.
|
||||
OOM {
|
||||
/// Native stack backtrace at the time the OOM occurred
|
||||
backtrace: Backtrace,
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
/// Construct a new Wasm trap with the given source location and trap code.
|
||||
/// Construct a new Wasm trap with the given trap code.
|
||||
///
|
||||
/// Internally saves a backtrace when constructed.
|
||||
/// Internally saves a backtrace when passed across a setjmp boundary, if the
|
||||
/// engine is configured to save backtraces.
|
||||
pub fn wasm(trap_code: TrapCode) -> Self {
|
||||
Trap::Wasm {
|
||||
trap_code,
|
||||
backtrace: Backtrace::new(),
|
||||
backtrace: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a new OOM trap with the given source location and trap code.
|
||||
/// Construct a new Wasm trap from a user Error.
|
||||
///
|
||||
/// Internally saves a backtrace when constructed.
|
||||
/// Internally saves a backtrace when passed across a setjmp boundary, if the
|
||||
/// engine is configured to save backtraces.
|
||||
pub fn user(error: Error) -> Self {
|
||||
Trap::User {
|
||||
error,
|
||||
backtrace: None,
|
||||
}
|
||||
}
|
||||
/// Construct a new OOM trap.
|
||||
///
|
||||
/// Internally saves a backtrace when passed across a setjmp boundary, if the
|
||||
/// engine is configured to save backtraces.
|
||||
pub fn oom() -> Self {
|
||||
Trap::OOM {
|
||||
backtrace: Backtrace::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A crate-local backtrace type which conditionally, at compile time, actually
|
||||
/// contains a backtrace from the `backtrace` crate or nothing.
|
||||
#[derive(Debug)]
|
||||
pub struct Backtrace {
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
trace: backtrace::Backtrace,
|
||||
}
|
||||
|
||||
impl Backtrace {
|
||||
/// Captures a new backtrace
|
||||
///
|
||||
/// Note that this function does nothing if the `wasm-backtrace` feature is
|
||||
/// disabled.
|
||||
pub fn new() -> Backtrace {
|
||||
Backtrace {
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
trace: backtrace::Backtrace::new_unresolved(),
|
||||
}
|
||||
Trap::OOM { backtrace: None }
|
||||
}
|
||||
|
||||
/// Returns the backtrace frames associated with this backtrace. Note that
|
||||
/// this is conditionally defined and not present when `wasm-backtrace` is
|
||||
/// not present.
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
pub fn frames(&self) -> &[backtrace::BacktraceFrame] {
|
||||
self.trace.frames()
|
||||
fn insert_backtrace(&mut self, bt: Backtrace) {
|
||||
match self {
|
||||
Trap::User { backtrace, .. } => *backtrace = Some(bt),
|
||||
Trap::Jit { backtrace, .. } => *backtrace = Some(bt),
|
||||
Trap::Wasm { backtrace, .. } => *backtrace = Some(bt),
|
||||
Trap::OOM { backtrace, .. } => *backtrace = Some(bt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,13 +189,14 @@ impl Backtrace {
|
||||
/// Highly unsafe since `closure` won't have any dtors run.
|
||||
pub unsafe fn catch_traps<'a, F>(
|
||||
signal_handler: Option<*const SignalHandler<'static>>,
|
||||
capture_backtrace: bool,
|
||||
callee: *mut VMContext,
|
||||
mut closure: F,
|
||||
) -> Result<(), Box<Trap>>
|
||||
where
|
||||
F: FnMut(*mut VMContext),
|
||||
{
|
||||
return CallThreadState::new(signal_handler).with(|cx| {
|
||||
return CallThreadState::new(signal_handler, capture_backtrace).with(|cx| {
|
||||
wasmtime_setjmp(
|
||||
cx.jmp_buf.as_ptr(),
|
||||
call_closure::<F>,
|
||||
@@ -219,29 +216,34 @@ where
|
||||
/// Temporary state stored on the stack which is registered in the `tls` module
|
||||
/// below for calls into wasm.
|
||||
pub struct CallThreadState {
|
||||
unwind: UnsafeCell<MaybeUninit<UnwindReason>>,
|
||||
unwind: UnsafeCell<MaybeUninit<(UnwindReason, Option<Backtrace>)>>,
|
||||
jmp_buf: Cell<*const u8>,
|
||||
handling_trap: Cell<bool>,
|
||||
signal_handler: Option<*const SignalHandler<'static>>,
|
||||
prev: Cell<tls::Ptr>,
|
||||
capture_backtrace: bool,
|
||||
}
|
||||
|
||||
enum UnwindReason {
|
||||
Panic(Box<dyn Any + Send>),
|
||||
UserTrap(Error),
|
||||
LibTrap(Trap),
|
||||
JitTrap { backtrace: Backtrace, pc: usize },
|
||||
JitTrap { pc: usize }, // Removed a backtrace here
|
||||
}
|
||||
|
||||
impl CallThreadState {
|
||||
#[inline]
|
||||
fn new(signal_handler: Option<*const SignalHandler<'static>>) -> CallThreadState {
|
||||
fn new(
|
||||
signal_handler: Option<*const SignalHandler<'static>>,
|
||||
capture_backtrace: bool,
|
||||
) -> CallThreadState {
|
||||
CallThreadState {
|
||||
unwind: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
jmp_buf: Cell::new(ptr::null()),
|
||||
handling_trap: Cell::new(false),
|
||||
signal_handler,
|
||||
prev: Cell::new(ptr::null()),
|
||||
capture_backtrace,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,16 +259,26 @@ impl CallThreadState {
|
||||
#[cold]
|
||||
unsafe fn read_trap(&self) -> Box<Trap> {
|
||||
Box::new(match (*self.unwind.get()).as_ptr().read() {
|
||||
UnwindReason::UserTrap(data) => Trap::User(data),
|
||||
UnwindReason::LibTrap(trap) => trap,
|
||||
UnwindReason::JitTrap { backtrace, pc } => Trap::Jit { pc, backtrace },
|
||||
UnwindReason::Panic(panic) => std::panic::resume_unwind(panic),
|
||||
(UnwindReason::UserTrap(error), backtrace) => Trap::User { error, backtrace },
|
||||
(UnwindReason::LibTrap(mut trap), backtrace) => {
|
||||
if let Some(backtrace) = backtrace {
|
||||
trap.insert_backtrace(backtrace);
|
||||
}
|
||||
trap
|
||||
}
|
||||
(UnwindReason::JitTrap { pc }, backtrace) => Trap::Jit { pc, backtrace },
|
||||
(UnwindReason::Panic(panic), _) => std::panic::resume_unwind(panic),
|
||||
})
|
||||
}
|
||||
|
||||
fn unwind_with(&self, reason: UnwindReason) -> ! {
|
||||
let backtrace = if self.capture_backtrace {
|
||||
Some(Backtrace::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
unsafe {
|
||||
(*self.unwind.get()).as_mut_ptr().write(reason);
|
||||
(*self.unwind.get()).as_mut_ptr().write((reason, backtrace));
|
||||
wasmtime_longjmp(self.jmp_buf.get());
|
||||
}
|
||||
}
|
||||
@@ -327,14 +339,15 @@ impl CallThreadState {
|
||||
}
|
||||
|
||||
fn capture_backtrace(&self, pc: *const u8) {
|
||||
let backtrace = Backtrace::new();
|
||||
let backtrace = if self.capture_backtrace {
|
||||
Some(Backtrace::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
unsafe {
|
||||
(*self.unwind.get())
|
||||
.as_mut_ptr()
|
||||
.write(UnwindReason::JitTrap {
|
||||
backtrace,
|
||||
pc: pc as usize,
|
||||
});
|
||||
.write((UnwindReason::JitTrap { pc: pc as usize }, backtrace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ anyhow = "1.0.19"
|
||||
region = "2.2.0"
|
||||
libc = "0.2"
|
||||
cfg-if = "1.0"
|
||||
backtrace = { version = "0.3.61", optional = true }
|
||||
backtrace = { version = "0.3.61" }
|
||||
log = "0.4.8"
|
||||
wat = { version = "1.0.43", optional = true }
|
||||
serde = { version = "1.0.94", features = ["derive"] }
|
||||
@@ -37,7 +37,7 @@ lazy_static = "1.4"
|
||||
rayon = { version = "1.0", optional = true }
|
||||
object = { version = "0.28", default-features = false, features = ['read_core', 'elf'] }
|
||||
async-trait = { version = "0.1.51", optional = true }
|
||||
once_cell = "1.9"
|
||||
once_cell = "1.12"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.3.7"
|
||||
@@ -61,7 +61,6 @@ default = [
|
||||
'pooling-allocator',
|
||||
'memory-init-cow',
|
||||
'vtune',
|
||||
'wasm-backtrace',
|
||||
]
|
||||
|
||||
# An on-by-default feature enabling runtime compilation of WebAssembly modules
|
||||
@@ -106,12 +105,6 @@ posix-signals-on-macos = ["wasmtime-runtime/posix-signals-on-macos"]
|
||||
# Enabling this feature has no effect on unsupported platforms.
|
||||
memory-init-cow = ["wasmtime-runtime/memory-init-cow"]
|
||||
|
||||
# Enables runtime support necessary to capture backtraces of WebAssembly code
|
||||
# that is running.
|
||||
#
|
||||
# This is enabled by default.
|
||||
wasm-backtrace = ["wasmtime-runtime/wasm-backtrace", "backtrace"]
|
||||
|
||||
# Enables in-progress support for the component model. Note that this feature is
|
||||
# in-progress, buggy, and incomplete. This is primarily here for internal
|
||||
# testing purposes.
|
||||
|
||||
@@ -91,6 +91,7 @@ pub struct Config {
|
||||
pub(crate) allocation_strategy: InstanceAllocationStrategy,
|
||||
pub(crate) max_wasm_stack: usize,
|
||||
pub(crate) features: WasmFeatures,
|
||||
pub(crate) wasm_backtrace: bool,
|
||||
pub(crate) wasm_backtrace_details_env_used: bool,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_stack_size: usize,
|
||||
@@ -124,6 +125,7 @@ impl Config {
|
||||
// 1` forces this), or at least it passed when this change was
|
||||
// committed.
|
||||
max_wasm_stack: 512 * 1024,
|
||||
wasm_backtrace: true,
|
||||
wasm_backtrace_details_env_used: false,
|
||||
features: WasmFeatures::default(),
|
||||
#[cfg(feature = "async")]
|
||||
@@ -140,9 +142,7 @@ impl Config {
|
||||
ret.cranelift_debug_verifier(false);
|
||||
ret.cranelift_opt_level(OptLevel::Speed);
|
||||
}
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
ret.wasm_reference_types(true);
|
||||
ret.features.reference_types = cfg!(feature = "wasm-backtrace");
|
||||
ret.wasm_multi_value(true);
|
||||
ret.wasm_bulk_memory(true);
|
||||
ret.wasm_simd(true);
|
||||
@@ -279,6 +279,35 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether backtraces exist in a `Trap`.
|
||||
///
|
||||
/// Enabled by default, this feature builds in support to
|
||||
/// generate backtraces at runtime for WebAssembly modules. This means that
|
||||
/// unwinding information is compiled into wasm modules and necessary runtime
|
||||
/// dependencies are enabled as well.
|
||||
///
|
||||
/// When disabled, wasm backtrace details are ignored, and [`crate::Trap::trace()`]
|
||||
/// will always return `None`.
|
||||
|
||||
pub fn wasm_backtrace(&mut self, enable: bool) -> &mut Self {
|
||||
self.wasm_backtrace = enable;
|
||||
#[cfg(compiler)]
|
||||
{
|
||||
// unwind_info must be enabled when either backtraces or reference types are enabled:
|
||||
self.compiler
|
||||
.set(
|
||||
"unwind_info",
|
||||
if enable || self.features.reference_types {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether backtraces in `Trap` will parse debug info in the wasm file to
|
||||
/// have filename/line number information.
|
||||
///
|
||||
@@ -508,14 +537,9 @@ impl Config {
|
||||
/// Note that enabling the reference types feature will also enable the bulk
|
||||
/// memory feature.
|
||||
///
|
||||
/// This feature is `true` by default. If the `wasm-backtrace` feature is
|
||||
/// disabled at compile time, however, then this is `false` by default and
|
||||
/// it cannot be turned on since GC currently requires backtraces to work.
|
||||
/// Note that the `wasm-backtrace` feature is on by default, however.
|
||||
/// This feature is `true` by default.
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/reference-types
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "wasm-backtrace")))]
|
||||
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
|
||||
self.features.reference_types = enable;
|
||||
|
||||
@@ -524,6 +548,17 @@ impl Config {
|
||||
self.compiler
|
||||
.set("enable_safepoints", if enable { "true" } else { "false" })
|
||||
.unwrap();
|
||||
// unwind_info must be enabled when either backtraces or reference types are enabled:
|
||||
self.compiler
|
||||
.set(
|
||||
"unwind_info",
|
||||
if enable || self.wasm_backtrace {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// The reference types proposal depends on the bulk memory proposal.
|
||||
@@ -1288,20 +1323,9 @@ impl Config {
|
||||
|
||||
#[cfg(compiler)]
|
||||
fn compiler_builder(strategy: Strategy) -> Result<Box<dyn CompilerBuilder>> {
|
||||
let mut builder = match strategy {
|
||||
Strategy::Auto | Strategy::Cranelift => wasmtime_cranelift::builder(),
|
||||
};
|
||||
builder
|
||||
.set(
|
||||
"unwind_info",
|
||||
if cfg!(feature = "wasm-backtrace") {
|
||||
"true"
|
||||
} else {
|
||||
"false"
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
Ok(builder)
|
||||
match strategy {
|
||||
Strategy::Auto | Strategy::Cranelift => Ok(wasmtime_cranelift::builder()),
|
||||
}
|
||||
}
|
||||
|
||||
fn round_up_to_pages(val: u64) -> u64 {
|
||||
@@ -1331,6 +1355,7 @@ impl Clone for Config {
|
||||
mem_creator: self.mem_creator.clone(),
|
||||
allocation_strategy: self.allocation_strategy.clone(),
|
||||
max_wasm_stack: self.max_wasm_stack,
|
||||
wasm_backtrace: self.wasm_backtrace,
|
||||
wasm_backtrace_details_env_used: self.wasm_backtrace_details_env_used,
|
||||
async_support: self.async_support,
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
@@ -303,7 +303,7 @@ impl Engine {
|
||||
// can affect the way the generated code performs or behaves at
|
||||
// runtime.
|
||||
"avoid_div_traps" => *value == FlagValue::Bool(true),
|
||||
"unwind_info" => *value == FlagValue::Bool(cfg!(feature = "wasm-backtrace")),
|
||||
"unwind_info" => *value == FlagValue::Bool(true),
|
||||
"libcall_call_conv" => *value == FlagValue::Enum("isa_default".into()),
|
||||
|
||||
// Features wasmtime doesn't use should all be disabled, since
|
||||
|
||||
@@ -1235,6 +1235,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
|
||||
}
|
||||
let result = wasmtime_runtime::catch_traps(
|
||||
store.0.signal_handler(),
|
||||
store.0.engine().config().wasm_backtrace,
|
||||
store.0.default_callee(),
|
||||
closure,
|
||||
);
|
||||
|
||||
@@ -290,14 +290,6 @@
|
||||
//! run-time via [`Config::memory_init_cow`] (which is also enabled by
|
||||
//! default).
|
||||
//!
|
||||
//! * `wasm-backtrace` - Enabled by default, this feature builds in support to
|
||||
//! generate backtraces at runtime for WebAssembly modules. This means that
|
||||
//! unwinding information is compiled into wasm modules and necessary runtime
|
||||
//! dependencies are enabled as well. If this is turned off then some methods
|
||||
//! to look at trap frames will not be available. Additionally at this time
|
||||
//! disabling this feature means that the reference types feature is always
|
||||
//! disabled as well.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! In addition to the examples below be sure to check out the [online embedding
|
||||
|
||||
@@ -1914,11 +1914,7 @@ unsafe impl<T> wasmtime_runtime::Store for StoreInner<T> {
|
||||
fn new_epoch(&mut self) -> Result<u64, anyhow::Error> {
|
||||
return match &mut self.epoch_deadline_behavior {
|
||||
EpochDeadline::Trap => {
|
||||
let trap = Trap::new_wasm(
|
||||
None,
|
||||
wasmtime_environ::TrapCode::Interrupt,
|
||||
wasmtime_runtime::Backtrace::new(),
|
||||
);
|
||||
let trap = Trap::new_wasm(wasmtime_environ::TrapCode::Interrupt, None);
|
||||
Err(anyhow::Error::from(trap))
|
||||
}
|
||||
EpochDeadline::Callback(callback) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::module::GlobalModuleRegistry;
|
||||
use crate::FrameInfo;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasmtime_environ::TrapCode as EnvTrapCode;
|
||||
@@ -127,95 +128,18 @@ impl fmt::Display for TrapCode {
|
||||
}
|
||||
}
|
||||
|
||||
struct TrapInner {
|
||||
reason: TrapReason,
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TrapBacktrace {
|
||||
wasm_trace: Vec<FrameInfo>,
|
||||
native_trace: Backtrace,
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
hint_wasm_backtrace_details_env: bool,
|
||||
}
|
||||
|
||||
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
|
||||
(t, t)
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
/// Creates a new `Trap` with `message`.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let trap = wasmtime::Trap::new("unexpected error");
|
||||
/// assert!(trap.to_string().contains("unexpected error"));
|
||||
/// ```
|
||||
#[cold] // traps are exceptional, this helps move handling off the main path
|
||||
pub fn new<I: Into<String>>(message: I) -> Self {
|
||||
let reason = TrapReason::Message(message.into());
|
||||
Trap::new_with_trace(None, reason, Backtrace::new())
|
||||
}
|
||||
|
||||
/// Creates a new `Trap` representing an explicit program exit with a classic `i32`
|
||||
/// exit status value.
|
||||
#[cold] // see Trap::new
|
||||
pub fn i32_exit(status: i32) -> Self {
|
||||
Trap::new_with_trace(None, TrapReason::I32Exit(status), Backtrace::new())
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn from_runtime_box(runtime_trap: Box<wasmtime_runtime::Trap>) -> Self {
|
||||
Self::from_runtime(*runtime_trap)
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self {
|
||||
match runtime_trap {
|
||||
wasmtime_runtime::Trap::User(error) => Trap::from(error),
|
||||
wasmtime_runtime::Trap::Jit { pc, backtrace } => {
|
||||
let code = GlobalModuleRegistry::with(|modules| {
|
||||
modules
|
||||
.lookup_trap_code(pc)
|
||||
.unwrap_or(EnvTrapCode::StackOverflow)
|
||||
});
|
||||
Trap::new_wasm(Some(pc), code, backtrace)
|
||||
}
|
||||
wasmtime_runtime::Trap::Wasm {
|
||||
trap_code,
|
||||
backtrace,
|
||||
} => Trap::new_wasm(None, trap_code, backtrace),
|
||||
wasmtime_runtime::Trap::OOM { backtrace } => {
|
||||
let reason = TrapReason::Message("out of memory".to_string());
|
||||
Trap::new_with_trace(None, reason, backtrace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn new_wasm(
|
||||
trap_pc: Option<usize>,
|
||||
code: EnvTrapCode,
|
||||
backtrace: Backtrace,
|
||||
) -> Self {
|
||||
let code = TrapCode::from_non_user(code);
|
||||
Trap::new_with_trace(trap_pc, TrapReason::InstructionTrap(code), backtrace)
|
||||
}
|
||||
|
||||
/// Creates a new `Trap`.
|
||||
///
|
||||
/// * `trap_pc` - this is the precise program counter, if available, that
|
||||
/// wasm trapped at. This is used when learning about the wasm stack trace
|
||||
/// to ensure we assign the correct source to every frame.
|
||||
///
|
||||
/// * `reason` - this is the wasmtime-internal reason for why this trap is
|
||||
/// being created.
|
||||
///
|
||||
/// * `native_trace` - this is a captured backtrace from when the trap
|
||||
/// occurred, and this will iterate over the frames to find frames that
|
||||
/// lie in wasm jit code.
|
||||
#[cfg_attr(not(feature = "wasm-backtrace"), allow(unused_mut, unused_variables))]
|
||||
fn new_with_trace(trap_pc: Option<usize>, reason: TrapReason, native_trace: Backtrace) -> Self {
|
||||
impl TrapBacktrace {
|
||||
pub fn new(native_trace: Backtrace, trap_pc: Option<usize>) -> Self {
|
||||
let mut wasm_trace = Vec::<FrameInfo>::new();
|
||||
let mut hint_wasm_backtrace_details_env = false;
|
||||
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
GlobalModuleRegistry::with(|registry| {
|
||||
for frame in native_trace.frames() {
|
||||
let pc = frame.ip() as usize;
|
||||
@@ -250,15 +174,103 @@ impl Trap {
|
||||
}
|
||||
}
|
||||
});
|
||||
Self {
|
||||
wasm_trace,
|
||||
native_trace,
|
||||
hint_wasm_backtrace_details_env,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TrapInner {
|
||||
reason: TrapReason,
|
||||
backtrace: OnceCell<TrapBacktrace>,
|
||||
}
|
||||
|
||||
fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
|
||||
(t, t)
|
||||
}
|
||||
|
||||
impl Trap {
|
||||
/// Creates a new `Trap` with `message`.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let trap = wasmtime::Trap::new("unexpected error");
|
||||
/// assert!(trap.to_string().contains("unexpected error"));
|
||||
/// ```
|
||||
#[cold] // traps are exceptional, this helps move handling off the main path
|
||||
pub fn new<I: Into<String>>(message: I) -> Self {
|
||||
let reason = TrapReason::Message(message.into());
|
||||
Trap::new_with_trace(reason, None)
|
||||
}
|
||||
|
||||
/// Creates a new `Trap` representing an explicit program exit with a classic `i32`
|
||||
/// exit status value.
|
||||
#[cold] // see Trap::new
|
||||
pub fn i32_exit(status: i32) -> Self {
|
||||
Trap::new_with_trace(TrapReason::I32Exit(status), None)
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn from_runtime_box(runtime_trap: Box<wasmtime_runtime::Trap>) -> Self {
|
||||
Self::from_runtime(*runtime_trap)
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn from_runtime(runtime_trap: wasmtime_runtime::Trap) -> Self {
|
||||
match runtime_trap {
|
||||
wasmtime_runtime::Trap::User { error, backtrace } => {
|
||||
let trap = Trap::from(error);
|
||||
if let Some(backtrace) = backtrace {
|
||||
trap.record_backtrace(TrapBacktrace::new(backtrace, None));
|
||||
}
|
||||
trap
|
||||
}
|
||||
wasmtime_runtime::Trap::Jit { pc, backtrace } => {
|
||||
let code = GlobalModuleRegistry::with(|modules| {
|
||||
modules
|
||||
.lookup_trap_code(pc)
|
||||
.unwrap_or(EnvTrapCode::StackOverflow)
|
||||
});
|
||||
let backtrace = backtrace.map(|bt| TrapBacktrace::new(bt, Some(pc)));
|
||||
Trap::new_wasm(code, backtrace)
|
||||
}
|
||||
wasmtime_runtime::Trap::Wasm {
|
||||
trap_code,
|
||||
backtrace,
|
||||
} => {
|
||||
let backtrace = backtrace.map(|bt| TrapBacktrace::new(bt, None));
|
||||
Trap::new_wasm(trap_code, backtrace)
|
||||
}
|
||||
wasmtime_runtime::Trap::OOM { backtrace } => {
|
||||
let reason = TrapReason::Message("out of memory".to_string());
|
||||
let backtrace = backtrace.map(|bt| TrapBacktrace::new(bt, None));
|
||||
Trap::new_with_trace(reason, backtrace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cold] // see Trap::new
|
||||
pub(crate) fn new_wasm(code: EnvTrapCode, backtrace: Option<TrapBacktrace>) -> Self {
|
||||
let code = TrapCode::from_non_user(code);
|
||||
Trap::new_with_trace(TrapReason::InstructionTrap(code), backtrace)
|
||||
}
|
||||
|
||||
/// Creates a new `Trap`.
|
||||
/// * `reason` - this is the wasmtime-internal reason for why this trap is
|
||||
/// being created.
|
||||
///
|
||||
/// * `backtrace` - this is a captured backtrace from when the trap
|
||||
/// occurred. Contains the native backtrace, and the backtrace of
|
||||
/// WebAssembly frames.
|
||||
fn new_with_trace(reason: TrapReason, backtrace: Option<TrapBacktrace>) -> Self {
|
||||
let backtrace = if let Some(bt) = backtrace {
|
||||
OnceCell::with_value(bt)
|
||||
} else {
|
||||
OnceCell::new()
|
||||
};
|
||||
Trap {
|
||||
inner: Arc::new(TrapInner {
|
||||
reason,
|
||||
native_trace,
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
wasm_trace,
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
hint_wasm_backtrace_details_env,
|
||||
}),
|
||||
inner: Arc::new(TrapInner { reason, backtrace }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,10 +295,12 @@ impl Trap {
|
||||
|
||||
/// Returns a list of function frames in WebAssembly code that led to this
|
||||
/// trap happening.
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
#[cfg_attr(nightlydoc, doc(cfg(feature = "wasm-backtrace")))]
|
||||
pub fn trace(&self) -> &[FrameInfo] {
|
||||
&self.inner.wasm_trace
|
||||
pub fn trace(&self) -> Option<&[FrameInfo]> {
|
||||
self.inner
|
||||
.backtrace
|
||||
.get()
|
||||
.as_ref()
|
||||
.map(|bt| bt.wasm_trace.as_slice())
|
||||
}
|
||||
|
||||
/// Code of a trap that happened while executing a WASM instruction.
|
||||
@@ -297,16 +311,26 @@ impl Trap {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn record_backtrace(&self, backtrace: TrapBacktrace) {
|
||||
// When a trap is created on top of the wasm stack, the trampoline will
|
||||
// re-raise it via
|
||||
// `wasmtime_runtime::raise_user_trap(trap.into::<Box<dyn Error>>())`
|
||||
// after panic::catch_unwind. We don't want to overwrite the first
|
||||
// backtrace recorded, as it is most precise.
|
||||
// FIXME: make sure backtraces are only created once per trap! they are
|
||||
// actually kinda expensive to create.
|
||||
let _ = self.inner.backtrace.try_insert(backtrace);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Trap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut f = f.debug_struct("Trap");
|
||||
f.field("reason", &self.inner.reason);
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
{
|
||||
f.field("wasm_trace", &self.inner.wasm_trace)
|
||||
.field("native_trace", &self.inner.native_trace);
|
||||
if let Some(backtrace) = self.inner.backtrace.get() {
|
||||
f.field("wasm_trace", &backtrace.wasm_trace)
|
||||
.field("native_trace", &backtrace.native_trace);
|
||||
}
|
||||
f.finish()
|
||||
}
|
||||
@@ -316,15 +340,13 @@ impl fmt::Display for Trap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.inner.reason)?;
|
||||
|
||||
#[cfg(feature = "wasm-backtrace")]
|
||||
{
|
||||
let trace = self.trace();
|
||||
if let Some(trace) = self.trace() {
|
||||
if trace.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
writeln!(f, "\nwasm backtrace:")?;
|
||||
|
||||
for (i, frame) in self.trace().iter().enumerate() {
|
||||
for (i, frame) in trace.iter().enumerate() {
|
||||
let name = frame.module_name().unwrap_or("<unknown>");
|
||||
write!(f, " {:>3}: ", i)?;
|
||||
|
||||
@@ -369,7 +391,13 @@ impl fmt::Display for Trap {
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.inner.hint_wasm_backtrace_details_env {
|
||||
if self
|
||||
.inner
|
||||
.backtrace
|
||||
.get()
|
||||
.map(|t| t.hint_wasm_backtrace_details_env)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
writeln!(f, "note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable to may show more debugging information")?;
|
||||
}
|
||||
}
|
||||
@@ -404,7 +432,7 @@ impl From<Box<dyn std::error::Error + Send + Sync>> for Trap {
|
||||
trap.clone()
|
||||
} else {
|
||||
let reason = TrapReason::Error(e.into());
|
||||
Trap::new_with_trace(None, reason, Backtrace::new())
|
||||
Trap::new_with_trace(reason, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ fn test_trap_trace() -> Result<()> {
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
let trace = e.trace().expect("backtrace is available");
|
||||
assert_eq!(trace.len(), 2);
|
||||
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
|
||||
assert_eq!(trace[0].func_index(), 1);
|
||||
@@ -70,6 +70,32 @@ fn test_trap_trace() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trap_backtrace_disabled() -> Result<()> {
|
||||
let mut config = Config::default();
|
||||
config.wasm_backtrace(false);
|
||||
let engine = Engine::new(&config).unwrap();
|
||||
let mut store = Store::<()>::new(&engine, ());
|
||||
let wat = r#"
|
||||
(module $hello_mod
|
||||
(func (export "run") (call $hello))
|
||||
(func $hello (unreachable))
|
||||
)
|
||||
"#;
|
||||
|
||||
let module = Module::new(store.engine(), wat)?;
|
||||
let instance = Instance::new(&mut store, &module, &[])?;
|
||||
let run_func = instance.get_typed_func::<(), (), _>(&mut store, "run")?;
|
||||
|
||||
let e = run_func
|
||||
.call(&mut store, ())
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
|
||||
assert!(e.trace().is_none(), "backtraces should be disabled");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(all(target_os = "macos", target_arch = "aarch64"), ignore)] // TODO #2808 system libunwind is broken on aarch64
|
||||
fn test_trap_trace_cb() -> Result<()> {
|
||||
@@ -94,7 +120,7 @@ fn test_trap_trace_cb() -> Result<()> {
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
let trace = e.trace().expect("backtrace is available");
|
||||
assert_eq!(trace.len(), 2);
|
||||
assert_eq!(trace[0].module_name().unwrap(), "hello_mod");
|
||||
assert_eq!(trace[0].func_index(), 2);
|
||||
@@ -124,7 +150,7 @@ fn test_trap_stack_overflow() -> Result<()> {
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
let trace = e.trace().expect("backtrace is available");
|
||||
assert!(trace.len() >= 32);
|
||||
for i in 0..trace.len() {
|
||||
assert_eq!(trace[i].module_name().unwrap(), "rec_mod");
|
||||
@@ -429,8 +455,9 @@ fn present_after_module_drop() -> Result<()> {
|
||||
|
||||
fn assert_trap(t: Trap) {
|
||||
println!("{}", t);
|
||||
assert_eq!(t.trace().len(), 1);
|
||||
assert_eq!(t.trace()[0].func_index(), 0);
|
||||
let trace = t.trace().expect("backtrace is available");
|
||||
assert_eq!(trace.len(), 1);
|
||||
assert_eq!(trace[0].func_index(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,7 +552,7 @@ fn parse_dwarf_info() -> Result<()> {
|
||||
.downcast::<Trap>()?;
|
||||
|
||||
let mut found = false;
|
||||
for frame in trap.trace() {
|
||||
for frame in trap.trace().expect("backtrace is available") {
|
||||
for symbol in frame.symbols() {
|
||||
if let Some(file) = symbol.file() {
|
||||
if file.ends_with("input.rs") {
|
||||
@@ -661,7 +688,7 @@ fn traps_without_address_map() -> Result<()> {
|
||||
.err()
|
||||
.expect("error calling function");
|
||||
|
||||
let trace = e.trace();
|
||||
let trace = e.trace().expect("backtrace is available");
|
||||
assert_eq!(trace.len(), 2);
|
||||
assert_eq!(trace[0].func_name(), Some("hello"));
|
||||
assert_eq!(trace[0].func_index(), 1);
|
||||
|
||||
Reference in New Issue
Block a user