wasmer_compiler/engine/trap/stack.rs
1use super::frame_info::{FRAME_INFO, GlobalFrameInfo};
2use backtrace::Backtrace;
3use wasmer_types::{FrameInfo, TrapCode};
4use wasmer_vm::Trap;
5
6/// Given a `Trap`, this function returns the Wasm trace and the trap code.
7pub fn get_trace_and_trapcode(trap: &Trap) -> (Vec<FrameInfo>, Option<TrapCode>) {
8    let info = FRAME_INFO.read().unwrap();
9    match &trap {
10        // A user error
11        Trap::User(_err) => (wasm_trace(&info, None, &Backtrace::new_unresolved()), None),
12        // A trap caused by the VM being Out of Memory
13        Trap::OOM { backtrace } => (wasm_trace(&info, None, backtrace), None),
14        // A trap caused by an error on the generated machine code for a Wasm function
15        Trap::Wasm {
16            pc,
17            signal_trap,
18            backtrace,
19        } => {
20            let trap_code = info
21                .lookup_trap_info(*pc)
22                .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| {
23                    info.trap_code
24                });
25
26            (wasm_trace(&info, Some(*pc), backtrace), Some(trap_code))
27        }
28        // A trap triggered manually from the Wasmer runtime
29        Trap::Lib {
30            trap_code,
31            backtrace,
32        } => (wasm_trace(&info, None, backtrace), Some(*trap_code)),
33        // An uncaught exception
34        Trap::UncaughtException { backtrace, .. } => (
35            wasm_trace(&info, None, backtrace),
36            Some(TrapCode::UncaughtException),
37        ),
38    }
39}
40
41fn wasm_trace(
42    info: &GlobalFrameInfo,
43    trap_pc: Option<usize>,
44    backtrace: &Backtrace,
45) -> Vec<FrameInfo> {
46    // Let's construct the trace
47    backtrace
48        .frames()
49        .iter()
50        .filter_map(|frame| {
51            let pc = frame.ip() as usize;
52            if pc == 0 {
53                None
54            } else {
55                // Note that we need to be careful about the pc we pass in here to
56                // lookup frame information. This program counter is used to
57                // translate back to an original source location in the origin wasm
58                // module. If this pc is the exact pc that the trap happened at,
59                // then we look up that pc precisely. Otherwise backtrace
60                // information typically points at the pc *after* the call
61                // instruction (because otherwise it's likely a call instruction on
62                // the stack). In that case we want to lookup information for the
63                // previous instruction (the call instruction) so we subtract one as
64                // the lookup.
65                let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
66                Some(pc_to_lookup)
67            }
68        })
69        .filter_map(|pc| info.lookup_frame_info(pc))
70        .collect::<Vec<_>>()
71}