wasmer_compiler/engine/trap/
stack.rs

1use std::sync::atomic::Ordering;
2
3#[cfg(unix)]
4use crate::engine::unwind::EXIT_CALLED;
5
6use super::frame_info::{FRAME_INFO, GlobalFrameInfo};
7use backtrace::Backtrace;
8use wasmer_types::{FrameInfo, TrapCode};
9use wasmer_vm::Trap;
10
11/// Given a `Trap`, this function returns the Wasm trace and the trap code.
12pub fn get_trace_and_trapcode(trap: &Trap) -> (Vec<FrameInfo>, Option<TrapCode>) {
13    #[cfg(unix)]
14    // If the exit is called, we can't access the back-trace information any longer (#5877)
15    if EXIT_CALLED.load(Ordering::SeqCst) {
16        return (Vec::new(), None);
17    }
18
19    let info = FRAME_INFO.read().unwrap();
20    match &trap {
21        // A user error
22        Trap::User(_err) => (wasm_trace(&info, None, &Backtrace::new_unresolved()), None),
23        // A trap caused by the VM being Out of Memory
24        Trap::OOM { backtrace } => (wasm_trace(&info, None, backtrace), None),
25        // A trap caused by an error on the generated machine code for a Wasm function
26        Trap::Wasm {
27            pc,
28            signal_trap,
29            backtrace,
30        } => {
31            let trap_code = info
32                .lookup_trap_info(*pc)
33                .map_or(signal_trap.unwrap_or(TrapCode::StackOverflow), |info| {
34                    info.trap_code
35                });
36
37            (wasm_trace(&info, Some(*pc), backtrace), Some(trap_code))
38        }
39        // A trap triggered manually from the Wasmer runtime
40        Trap::Lib {
41            trap_code,
42            backtrace,
43        } => (wasm_trace(&info, None, backtrace), Some(*trap_code)),
44        // An uncaught exception
45        Trap::UncaughtException { backtrace, .. } => (
46            wasm_trace(&info, None, backtrace),
47            Some(TrapCode::UncaughtException),
48        ),
49    }
50}
51
52/// Captures the current Wasm stack trace. Only useful when
53/// there are active Wasm frames on the stack, such as in
54/// libcalls or imported functions.
55pub fn wasm_trace_from_current_stack() -> Vec<FrameInfo> {
56    let info = FRAME_INFO.read().unwrap();
57    let backtrace = Backtrace::new_unresolved();
58    wasm_trace(&info, None, &backtrace)
59}
60
61fn wasm_trace(
62    info: &GlobalFrameInfo,
63    trap_pc: Option<usize>,
64    backtrace: &Backtrace,
65) -> Vec<FrameInfo> {
66    // Let's construct the trace
67    backtrace
68        .frames()
69        .iter()
70        .filter_map(|frame| {
71            let pc = frame.ip() as usize;
72            if pc == 0 {
73                None
74            } else {
75                // Note that we need to be careful about the pc we pass in here to
76                // lookup frame information. This program counter is used to
77                // translate back to an original source location in the origin wasm
78                // module. If this pc is the exact pc that the trap happened at,
79                // then we look up that pc precisely. Otherwise backtrace
80                // information typically points at the pc *after* the call
81                // instruction (because otherwise it's likely a call instruction on
82                // the stack). In that case we want to lookup information for the
83                // previous instruction (the call instruction) so we subtract one as
84                // the lookup.
85                let pc_to_lookup = if Some(pc) == trap_pc { pc } else { pc - 1 };
86                Some(pc_to_lookup)
87            }
88        })
89        .filter_map(|pc| info.lookup_frame_info(pc))
90        .collect::<Vec<_>>()
91}