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