wasmer_compiler/engine/trap/
frame_info.rs1use 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, LazyLock, 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
31pub static FRAME_INFO: LazyLock<RwLock<GlobalFrameInfo>> = LazyLock::new(RwLock::default);
37
38#[derive(Default)]
39pub struct GlobalFrameInfo {
40 ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
50}
51
52#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
55pub struct GlobalFrameInfoRegistration {
56 key: usize,
59}
60
61#[derive(Debug)]
62struct ModuleInfoFrameInfo {
63 start: usize,
64 functions: BTreeMap<usize, FunctionInfo>,
65 module: Arc<ModuleInfo>,
66 frame_infos: FrameInfosVariant,
67}
68
69impl ModuleInfoFrameInfo {
70 fn function_debug_info(
71 &self,
72 local_index: LocalFunctionIndex,
73 ) -> CompiledFunctionFrameInfoVariant {
74 self.frame_infos.get(local_index).unwrap()
75 }
76
77 fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
79 let (end, func) = self.functions.range(pc..).next()?;
80 if func.start <= pc && pc <= *end {
81 Some(func)
82 } else {
83 None
84 }
85 }
86}
87
88#[derive(Debug)]
89struct FunctionInfo {
90 start: usize,
91 local_index: LocalFunctionIndex,
92}
93
94impl GlobalFrameInfo {
95 pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
100 let module = self.module_info(pc)?;
101 let func = module.function_info(pc)?;
102
103 let rel_pos = pc - func.start;
107 let debug_info = module.function_debug_info(func.local_index);
108 let instr_map = debug_info.address_map();
109 let pos = match instr_map.instructions().code_offset_by_key(rel_pos) {
110 Ok(pos) => Some(pos),
112
113 Err(0) => None,
116
117 Err(n) => {
123 let instr = &instr_map.instructions().get(n - 1);
124 if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
125 Some(n - 1)
126 } else {
127 None
128 }
129 }
130 };
131
132 let instr = match pos {
133 Some(pos) => instr_map.instructions().get(pos).srcloc,
134 None => instr_map.start_srcloc(),
139 };
140 let func_index = module.module.func_index(func.local_index);
141 Some(FrameInfo::new(
142 module.module.name(),
143 func_index.index() as u32,
144 module.module.function_names.get(&func_index).cloned(),
145 instr_map.start_srcloc(),
146 instr,
147 ))
148 }
149
150 pub fn lookup_trap_info(&self, pc: usize) -> Option<TrapInformation> {
152 let module = self.module_info(pc)?;
153 let func = module.function_info(pc)?;
154 let debug_info = module.function_debug_info(func.local_index);
155 let traps = debug_info.traps();
156 let idx = traps
157 .binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
158 .ok()?;
159 Some(traps[idx])
160 }
161
162 fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
164 let (end, module_info) = self.ranges.range(pc..).next()?;
165 if module_info.start <= pc && pc <= *end {
166 Some(module_info)
167 } else {
168 None
169 }
170 }
171}
172
173impl Drop for GlobalFrameInfoRegistration {
174 fn drop(&mut self) {
175 if let Ok(mut info) = FRAME_INFO.write() {
176 info.ranges.remove(&self.key);
177 }
178 }
179}
180
181#[derive(Debug)]
184#[repr(C)]
185pub struct FunctionExtent {
186 pub ptr: FunctionBodyPtr,
189 pub length: usize,
191}
192
193#[derive(Debug)]
196pub enum FrameInfosVariant {
197 Owned(PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>),
199 Archived(ArtifactBuildFromArchive),
201}
202
203impl FrameInfosVariant {
204 pub fn get(&self, index: LocalFunctionIndex) -> Option<CompiledFunctionFrameInfoVariant> {
206 match self {
207 Self::Owned(map) => map.get(index).map(CompiledFunctionFrameInfoVariant::Ref),
208 Self::Archived(archive) => archive
209 .get_frame_info_ref()
210 .get(index)
211 .map(CompiledFunctionFrameInfoVariant::Archived),
212 }
213 }
214}
215
216#[derive(Debug)]
218pub enum CompiledFunctionFrameInfoVariant<'a> {
219 Ref(&'a CompiledFunctionFrameInfo),
221 Archived(&'a ArchivedCompiledFunctionFrameInfo),
223}
224
225impl CompiledFunctionFrameInfoVariant<'_> {
226 pub fn address_map(&self) -> FunctionAddressMapVariant<'_> {
228 match self {
229 CompiledFunctionFrameInfoVariant::Ref(info) => {
230 FunctionAddressMapVariant::Ref(&info.address_map)
231 }
232 CompiledFunctionFrameInfoVariant::Archived(info) => {
233 FunctionAddressMapVariant::Archived(&info.address_map)
234 }
235 }
236 }
237
238 pub fn traps(&self) -> VecTrapInformationVariant {
240 match self {
241 CompiledFunctionFrameInfoVariant::Ref(info) => {
242 VecTrapInformationVariant::Ref(&info.traps)
243 }
244 CompiledFunctionFrameInfoVariant::Archived(info) => {
245 let traps = rkyv::deserialize::<_, rkyv::rancor::Error>(&info.traps).unwrap();
246 VecTrapInformationVariant::Owned(traps)
247 }
248 }
249 }
250}
251
252#[derive(Debug)]
254pub enum VecTrapInformationVariant<'a> {
255 Ref(&'a Vec<TrapInformation>),
256 Owned(Vec<TrapInformation>),
257}
258
259impl Deref for VecTrapInformationVariant<'_> {
261 type Target = [TrapInformation];
262
263 fn deref(&self) -> &Self::Target {
264 match self {
265 VecTrapInformationVariant::Ref(traps) => traps,
266 VecTrapInformationVariant::Owned(traps) => traps,
267 }
268 }
269}
270
271#[derive(Debug)]
272pub enum FunctionAddressMapVariant<'a> {
273 Ref(&'a FunctionAddressMap),
274 Archived(&'a ArchivedFunctionAddressMap),
275}
276
277impl FunctionAddressMapVariant<'_> {
278 pub fn instructions(&self) -> FunctionAddressMapInstructionVariant {
279 match self {
280 FunctionAddressMapVariant::Ref(map) => {
281 FunctionAddressMapInstructionVariant::Owned(&map.instructions)
282 }
283 FunctionAddressMapVariant::Archived(map) => {
284 FunctionAddressMapInstructionVariant::Archived(&map.instructions)
285 }
286 }
287 }
288
289 pub fn start_srcloc(&self) -> SourceLoc {
290 match self {
291 FunctionAddressMapVariant::Ref(map) => map.start_srcloc,
292 FunctionAddressMapVariant::Archived(map) => {
293 rkyv::deserialize::<_, rkyv::rancor::Error>(&map.start_srcloc).unwrap()
294 }
295 }
296 }
297
298 pub fn end_srcloc(&self) -> SourceLoc {
299 match self {
300 FunctionAddressMapVariant::Ref(map) => map.end_srcloc,
301 FunctionAddressMapVariant::Archived(map) => {
302 rkyv::deserialize::<_, rkyv::rancor::Error>(&map.end_srcloc).unwrap()
303 }
304 }
305 }
306
307 pub fn body_offset(&self) -> usize {
308 match self {
309 FunctionAddressMapVariant::Ref(map) => map.body_offset,
310 FunctionAddressMapVariant::Archived(map) => map.body_offset.to_native() as usize,
311 }
312 }
313
314 pub fn body_len(&self) -> usize {
315 match self {
316 FunctionAddressMapVariant::Ref(map) => map.body_len,
317 FunctionAddressMapVariant::Archived(map) => map.body_len.to_native() as usize,
318 }
319 }
320}
321
322#[derive(Debug)]
323pub enum FunctionAddressMapInstructionVariant<'a> {
324 Owned(&'a Vec<InstructionAddressMap>),
325 Archived(&'a ArchivedVec<ArchivedInstructionAddressMap>),
326}
327
328impl FunctionAddressMapInstructionVariant<'_> {
329 pub fn code_offset_by_key(&self, key: usize) -> Result<usize, usize> {
330 match self {
331 FunctionAddressMapInstructionVariant::Owned(instructions) => {
332 instructions.binary_search_by_key(&key, |map| map.code_offset)
333 }
334 FunctionAddressMapInstructionVariant::Archived(instructions) => {
335 instructions.binary_search_by_key(&key, |map| map.code_offset.to_native() as usize)
336 }
337 }
338 }
339
340 pub fn get(&self, index: usize) -> InstructionAddressMap {
341 match self {
342 FunctionAddressMapInstructionVariant::Owned(instructions) => instructions[index],
343 FunctionAddressMapInstructionVariant::Archived(instructions) => instructions
344 .get(index)
345 .map(|map| InstructionAddressMap {
346 srcloc: rkyv::deserialize::<_, rkyv::rancor::Error>(&map.srcloc).unwrap(),
347 code_offset: map.code_offset.to_native() as usize,
348 code_len: map.code_len.to_native() as usize,
349 })
350 .unwrap(),
351 }
352 }
353}
354
355pub fn register(
362 module: Arc<ModuleInfo>,
363 finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
364 frame_infos: FrameInfosVariant,
365) -> Option<GlobalFrameInfoRegistration> {
366 let mut min = usize::MAX;
367 let mut max = 0;
368 let mut functions = BTreeMap::new();
369 for (
370 i,
371 FunctionExtent {
372 ptr: start,
373 length: len,
374 },
375 ) in finished_functions.iter()
376 {
377 let start = **start as usize;
378 let end = start + len - 1;
380 min = cmp::min(min, start);
381 max = cmp::max(max, end);
382 let func = FunctionInfo {
383 start,
384 local_index: i,
385 };
386 assert!(functions.insert(end, func).is_none());
387 }
388 if functions.is_empty() {
389 return None;
390 }
391
392 let mut info = FRAME_INFO.write().unwrap();
393 if let Some((_, prev)) = info.ranges.range(max..).next() {
396 assert!(prev.start > max);
397 }
398 if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
399 assert!(*prev_end < min);
400 }
401
402 let prev = info.ranges.insert(
404 max,
405 ModuleInfoFrameInfo {
406 start: min,
407 functions,
408 module,
409 frame_infos,
410 },
411 );
412 assert!(prev.is_none());
413 Some(GlobalFrameInfoRegistration { key: max })
414}