wasmer_compiler/engine/trap/
frame_info.rs

1//! This module is used for having backtraces in the Wasm runtime.
2//! Once the Compiler has compiled the ModuleInfo, and we have a set of
3//! compiled functions (addresses and function index) and a module,
4//! then we can use this to set a backtrace for that module.
5//!
6//! # Example
7//! ```ignore
8//! use wasmer_vm::{FRAME_INFO};
9//! use wasmer_types::ModuleInfo;
10//!
11//! let module: ModuleInfo = ...;
12//! FRAME_INFO.register(module, compiled_functions);
13//! ```
14
15use crate::types::address_map::{
16    ArchivedFunctionAddressMap, ArchivedInstructionAddressMap, FunctionAddressMap,
17    InstructionAddressMap,
18};
19use crate::types::function::{ArchivedCompiledFunctionFrameInfo, CompiledFunctionFrameInfo};
20use crate::ArtifactBuildFromArchive;
21use rkyv::vec::ArchivedVec;
22use std::collections::BTreeMap;
23use std::sync::{Arc, RwLock};
24use wasmer_types::lib::std::{cmp, ops::Deref};
25use wasmer_types::{
26    entity::{BoxedSlice, EntityRef, PrimaryMap},
27    FrameInfo, LocalFunctionIndex, ModuleInfo, SourceLoc, TrapInformation,
28};
29use wasmer_vm::FunctionBodyPtr;
30
31lazy_static::lazy_static! {
32    /// This is a global cache of backtrace frame information for all active
33    ///
34    /// This global cache is used during `Trap` creation to symbolicate frames.
35    /// This is populated on module compilation, and it is cleared out whenever
36    /// all references to a module are dropped.
37    pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
38}
39
40#[derive(Default)]
41pub struct GlobalFrameInfo {
42    /// An internal map that keeps track of backtrace frame information for
43    /// each module.
44    ///
45    /// This map is morally a map of ranges to a map of information for that
46    /// module. Each module is expected to reside in a disjoint section of
47    /// contiguous memory. No modules can overlap.
48    ///
49    /// The key of this map is the highest address in the module and the value
50    /// is the module's information, which also contains the start address.
51    ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
52}
53
54/// An RAII structure used to unregister a module's frame information when the
55/// module is destroyed.
56#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
57pub struct GlobalFrameInfoRegistration {
58    /// The key that will be removed from the global `ranges` map when this is
59    /// dropped.
60    key: usize,
61}
62
63#[derive(Debug)]
64struct ModuleInfoFrameInfo {
65    start: usize,
66    functions: BTreeMap<usize, FunctionInfo>,
67    module: Arc<ModuleInfo>,
68    frame_infos: FrameInfosVariant,
69}
70
71impl ModuleInfoFrameInfo {
72    fn function_debug_info(
73        &self,
74        local_index: LocalFunctionIndex,
75    ) -> CompiledFunctionFrameInfoVariant {
76        self.frame_infos.get(local_index).unwrap()
77    }
78
79    /// Gets a function given a pc
80    fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
81        let (end, func) = self.functions.range(pc..).next()?;
82        if func.start <= pc && pc <= *end {
83            Some(func)
84        } else {
85            None
86        }
87    }
88}
89
90#[derive(Debug)]
91struct FunctionInfo {
92    start: usize,
93    local_index: LocalFunctionIndex,
94}
95
96impl GlobalFrameInfo {
97    /// Fetches frame information about a program counter in a backtrace.
98    ///
99    /// Returns an object if this `pc` is known to some previously registered
100    /// module, or returns `None` if no information can be found.
101    pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
102        let module = self.module_info(pc)?;
103        let func = module.function_info(pc)?;
104
105        // Use our relative position from the start of the function to find the
106        // machine instruction that corresponds to `pc`, which then allows us to
107        // map that to a wasm original source location.
108        let rel_pos = pc - func.start;
109        let debug_info = module.function_debug_info(func.local_index);
110        let instr_map = debug_info.address_map();
111        let pos = match instr_map.instructions().code_offset_by_key(rel_pos) {
112            // Exact hit!
113            Ok(pos) => Some(pos),
114
115            // This *would* be at the first slot in the array, so no
116            // instructions cover `pc`.
117            Err(0) => None,
118
119            // This would be at the `nth` slot, so check `n-1` to see if we're
120            // part of that instruction. This happens due to the minus one when
121            // this function is called form trap symbolication, where we don't
122            // always get called with a `pc` that's an exact instruction
123            // boundary.
124            Err(n) => {
125                let instr = &instr_map.instructions().get(n - 1);
126                if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
127                    Some(n - 1)
128                } else {
129                    None
130                }
131            }
132        };
133
134        let instr = match pos {
135            Some(pos) => instr_map.instructions().get(pos).srcloc,
136            // Some compilers don't emit yet the full trap information for each of
137            // the instructions (such as LLVM).
138            // In case no specific instruction is found, we return by default the
139            // start offset of the function.
140            None => instr_map.start_srcloc(),
141        };
142        let func_index = module.module.func_index(func.local_index);
143        Some(FrameInfo::new(
144            module.module.name(),
145            func_index.index() as u32,
146            module.module.function_names.get(&func_index).cloned(),
147            instr_map.start_srcloc(),
148            instr,
149        ))
150    }
151
152    /// Fetches trap information about a program counter in a backtrace.
153    pub fn lookup_trap_info(&self, pc: usize) -> Option<TrapInformation> {
154        let module = self.module_info(pc)?;
155        let func = module.function_info(pc)?;
156        let debug_info = module.function_debug_info(func.local_index);
157        let traps = debug_info.traps();
158        let idx = traps
159            .binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
160            .ok()?;
161        Some(traps[idx])
162    }
163
164    /// Gets a module given a pc
165    fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
166        let (end, module_info) = self.ranges.range(pc..).next()?;
167        if module_info.start <= pc && pc <= *end {
168            Some(module_info)
169        } else {
170            None
171        }
172    }
173}
174
175impl Drop for GlobalFrameInfoRegistration {
176    fn drop(&mut self) {
177        if let Ok(mut info) = FRAME_INFO.write() {
178            info.ranges.remove(&self.key);
179        }
180    }
181}
182
183/// Represents a continuous region of executable memory starting with a function
184/// entry point.
185#[derive(Debug)]
186#[repr(C)]
187pub struct FunctionExtent {
188    /// Entry point for normal entry of the function. All addresses in the
189    /// function lie after this address.
190    pub ptr: FunctionBodyPtr,
191    /// Length in bytes.
192    pub length: usize,
193}
194
195/// The variant of the frame information which can be an owned type
196/// or the explicit framed map
197#[derive(Debug)]
198pub enum FrameInfosVariant {
199    /// Owned frame infos
200    Owned(PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>),
201    /// Archived frame infos
202    Archived(ArtifactBuildFromArchive),
203}
204
205impl FrameInfosVariant {
206    /// Gets the frame info for a given local function index
207    pub fn get(&self, index: LocalFunctionIndex) -> Option<CompiledFunctionFrameInfoVariant> {
208        match self {
209            Self::Owned(map) => map.get(index).map(CompiledFunctionFrameInfoVariant::Ref),
210            Self::Archived(archive) => archive
211                .get_frame_info_ref()
212                .get(index)
213                .map(CompiledFunctionFrameInfoVariant::Archived),
214        }
215    }
216}
217
218/// The variant of the compiled function frame info which can be an owned type
219#[derive(Debug)]
220pub enum CompiledFunctionFrameInfoVariant<'a> {
221    /// A reference to the frame info
222    Ref(&'a CompiledFunctionFrameInfo),
223    /// An archived frame info
224    Archived(&'a ArchivedCompiledFunctionFrameInfo),
225}
226
227impl CompiledFunctionFrameInfoVariant<'_> {
228    /// Gets the address map for the frame info
229    pub fn address_map(&self) -> FunctionAddressMapVariant<'_> {
230        match self {
231            CompiledFunctionFrameInfoVariant::Ref(info) => {
232                FunctionAddressMapVariant::Ref(&info.address_map)
233            }
234            CompiledFunctionFrameInfoVariant::Archived(info) => {
235                FunctionAddressMapVariant::Archived(&info.address_map)
236            }
237        }
238    }
239
240    /// Gets the traps for the frame info
241    pub fn traps(&self) -> VecTrapInformationVariant {
242        match self {
243            CompiledFunctionFrameInfoVariant::Ref(info) => {
244                VecTrapInformationVariant::Ref(&info.traps)
245            }
246            CompiledFunctionFrameInfoVariant::Archived(info) => {
247                let traps = rkyv::deserialize::<_, rkyv::rancor::Error>(&info.traps).unwrap();
248                VecTrapInformationVariant::Owned(traps)
249            }
250        }
251    }
252}
253
254/// The variant of the trap information which can be an owned type
255#[derive(Debug)]
256pub enum VecTrapInformationVariant<'a> {
257    Ref(&'a Vec<TrapInformation>),
258    Owned(Vec<TrapInformation>),
259}
260
261// We need to implement it for the `Deref` in `wasmer_types` to support both `core` and `std`.
262impl Deref for VecTrapInformationVariant<'_> {
263    type Target = [TrapInformation];
264
265    fn deref(&self) -> &Self::Target {
266        match self {
267            VecTrapInformationVariant::Ref(traps) => traps,
268            VecTrapInformationVariant::Owned(traps) => traps,
269        }
270    }
271}
272
273#[derive(Debug)]
274pub enum FunctionAddressMapVariant<'a> {
275    Ref(&'a FunctionAddressMap),
276    Archived(&'a ArchivedFunctionAddressMap),
277}
278
279impl FunctionAddressMapVariant<'_> {
280    pub fn instructions(&self) -> FunctionAddressMapInstructionVariant {
281        match self {
282            FunctionAddressMapVariant::Ref(map) => {
283                FunctionAddressMapInstructionVariant::Owned(&map.instructions)
284            }
285            FunctionAddressMapVariant::Archived(map) => {
286                FunctionAddressMapInstructionVariant::Archived(&map.instructions)
287            }
288        }
289    }
290
291    pub fn start_srcloc(&self) -> SourceLoc {
292        match self {
293            FunctionAddressMapVariant::Ref(map) => map.start_srcloc,
294            FunctionAddressMapVariant::Archived(map) => {
295                rkyv::deserialize::<_, rkyv::rancor::Error>(&map.start_srcloc).unwrap()
296            }
297        }
298    }
299
300    pub fn end_srcloc(&self) -> SourceLoc {
301        match self {
302            FunctionAddressMapVariant::Ref(map) => map.end_srcloc,
303            FunctionAddressMapVariant::Archived(map) => {
304                rkyv::deserialize::<_, rkyv::rancor::Error>(&map.end_srcloc).unwrap()
305            }
306        }
307    }
308
309    pub fn body_offset(&self) -> usize {
310        match self {
311            FunctionAddressMapVariant::Ref(map) => map.body_offset,
312            FunctionAddressMapVariant::Archived(map) => map.body_offset.to_native() as usize,
313        }
314    }
315
316    pub fn body_len(&self) -> usize {
317        match self {
318            FunctionAddressMapVariant::Ref(map) => map.body_len,
319            FunctionAddressMapVariant::Archived(map) => map.body_len.to_native() as usize,
320        }
321    }
322}
323
324#[derive(Debug)]
325pub enum FunctionAddressMapInstructionVariant<'a> {
326    Owned(&'a Vec<InstructionAddressMap>),
327    Archived(&'a ArchivedVec<ArchivedInstructionAddressMap>),
328}
329
330impl FunctionAddressMapInstructionVariant<'_> {
331    pub fn code_offset_by_key(&self, key: usize) -> Result<usize, usize> {
332        match self {
333            FunctionAddressMapInstructionVariant::Owned(instructions) => {
334                instructions.binary_search_by_key(&key, |map| map.code_offset)
335            }
336            FunctionAddressMapInstructionVariant::Archived(instructions) => {
337                instructions.binary_search_by_key(&key, |map| map.code_offset.to_native() as usize)
338            }
339        }
340    }
341
342    pub fn get(&self, index: usize) -> InstructionAddressMap {
343        match self {
344            FunctionAddressMapInstructionVariant::Owned(instructions) => instructions[index],
345            FunctionAddressMapInstructionVariant::Archived(instructions) => instructions
346                .get(index)
347                .map(|map| InstructionAddressMap {
348                    srcloc: rkyv::deserialize::<_, rkyv::rancor::Error>(&map.srcloc).unwrap(),
349                    code_offset: map.code_offset.to_native() as usize,
350                    code_len: map.code_len.to_native() as usize,
351                })
352                .unwrap(),
353        }
354    }
355}
356
357/// Registers a new compiled module's frame information.
358///
359/// This function will register the `names` information for all of the
360/// compiled functions within `module`. If the `module` has no functions
361/// then `None` will be returned. Otherwise the returned object, when
362/// dropped, will be used to unregister all name information from this map.
363pub fn register(
364    module: Arc<ModuleInfo>,
365    finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
366    frame_infos: FrameInfosVariant,
367) -> Option<GlobalFrameInfoRegistration> {
368    let mut min = usize::MAX;
369    let mut max = 0;
370    let mut functions = BTreeMap::new();
371    for (
372        i,
373        FunctionExtent {
374            ptr: start,
375            length: len,
376        },
377    ) in finished_functions.iter()
378    {
379        let start = **start as usize;
380        // end is "last byte" of the function code
381        let end = start + len - 1;
382        min = cmp::min(min, start);
383        max = cmp::max(max, end);
384        let func = FunctionInfo {
385            start,
386            local_index: i,
387        };
388        assert!(functions.insert(end, func).is_none());
389    }
390    if functions.is_empty() {
391        return None;
392    }
393
394    let mut info = FRAME_INFO.write().unwrap();
395    // First up assert that our chunk of jit functions doesn't collide with
396    // any other known chunks of jit functions...
397    if let Some((_, prev)) = info.ranges.range(max..).next() {
398        assert!(prev.start > max);
399    }
400    if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
401        assert!(*prev_end < min);
402    }
403
404    // ... then insert our range and assert nothing was there previously
405    let prev = info.ranges.insert(
406        max,
407        ModuleInfoFrameInfo {
408            start: min,
409            functions,
410            module,
411            frame_infos,
412        },
413    );
414    assert!(prev.is_none());
415    Some(GlobalFrameInfoRegistration { key: max })
416}