wasmer_compiler/engine/trap/
frame_info.rs1use crate::ArtifactBuildFromArchive;
16use crate::engine::artifact::TrapReader;
17use crate::engine::mapped_binary::{DebugInfo, DebugInfoSource};
18use crate::types::address_map::{
19 ArchivedFunctionAddressMap, ArchivedInstructionAddressMap, FunctionAddressMap,
20 InstructionAddressMap,
21};
22use crate::types::function::{ArchivedCompiledFunctionFrameInfo, CompiledFunctionFrameInfo};
23use rkyv::vec::ArchivedVec;
24use std::collections::BTreeMap;
25use std::sync::{Arc, LazyLock, RwLock};
26use wasmer_types::lib::std::{cmp, ops::Deref};
27use wasmer_types::{
28 FrameInfo, LocalFunctionIndex, ModuleInfo, SourceLoc, TrapInformation,
29 entity::{BoxedSlice, EntityRef, PrimaryMap},
30};
31use wasmer_vm::FunctionBodyPtr;
32
33pub static FRAME_INFO: LazyLock<RwLock<GlobalFrameInfo>> = LazyLock::new(RwLock::default);
39
40#[derive(Default)]
41pub struct GlobalFrameInfo {
42 ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
52}
53
54#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
57pub struct GlobalFrameInfoRegistration {
58 key: usize,
61}
62
63struct ModuleInfoFrameInfo {
64 start: usize,
65 functions: BTreeMap<usize, FunctionInfo>,
66 module: Arc<ModuleInfo>,
67 frame_infos: Option<FrameInfosVariant>,
69 image_base: usize,
73 debug_info: DebugInfo,
75 trap_reader: Option<TrapReader>,
77}
78
79impl ModuleInfoFrameInfo {
80 fn function_debug_info(
81 &self,
82 local_index: LocalFunctionIndex,
83 ) -> Option<CompiledFunctionFrameInfoVariant<'_>> {
84 self.frame_infos.as_ref()?.get(local_index)
85 }
86
87 fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
89 let (end, func) = self.functions.range(pc..).next()?;
90 if func.start <= pc && pc <= *end {
91 Some(func)
92 } else {
93 None
94 }
95 }
96}
97
98#[derive(Debug)]
99struct FunctionInfo {
100 start: usize,
101 local_index: LocalFunctionIndex,
102}
103
104impl GlobalFrameInfo {
105 pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
110 let module = self.module_info(pc)?;
111 let func = module.function_info(pc)?;
112 let func_index = module.module.func_index(func.local_index);
113 let mut function_name = module.module.function_names.get(&func_index).cloned();
114
115 if let Some(debug_info) = module.function_debug_info(func.local_index) {
118 let rel_pos = pc - func.start;
122 let instr_map = debug_info.address_map();
123 let pos = match instr_map.instructions().code_offset_by_key(rel_pos) {
124 Ok(pos) => Some(pos),
126
127 Err(0) => None,
130
131 Err(n) => {
137 let instr = &instr_map.instructions().get(n - 1);
138 if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len
139 {
140 Some(n - 1)
141 } else {
142 None
143 }
144 }
145 };
146
147 let instr = match pos {
148 Some(pos) => instr_map.instructions().get(pos).srcloc,
149 None => instr_map.start_srcloc(),
154 };
155 return Some(FrameInfo::new(
156 module.module.name(),
157 func_index.index() as u32,
158 function_name,
159 instr_map.start_srcloc(),
160 instr,
161 ));
162 }
163
164 let get_line = |context: &addr2line::Context<crate::engine::mapped_binary::DwarfReader>,
168 pc: u64| {
169 if let Ok(Some(location)) = context.find_location(pc)
170 && let Some(line) = location.line
171 && let Some(line) = line.checked_sub(1)
172 {
173 SourceLoc::new(line)
174 } else {
175 SourceLoc::default()
176 }
177 };
178
179 let (func_start, instr, resolved_name) = module.debug_info.with_context(|context| {
180 let Some(context) = context else {
181 return (SourceLoc::default(), SourceLoc::default(), None);
182 };
183
184 let probe = (pc - module.image_base) as u64;
185 let instr = get_line(context, probe);
186 let func_start = get_line(context, (func.start - module.image_base) as u64);
187
188 let resolved_name = if let Ok(mut frames) = context.find_frames(probe).skip_all_loads()
189 && let Ok(Some(frame)) = frames.next()
190 && let Some(function) = frame.function
191 && let Ok(name) = function.raw_name()
192 && name != module.module.get_function_name(func_index)
193 {
194 Some(name.into_owned())
195 } else {
196 None
197 };
198
199 (func_start, instr, resolved_name)
200 });
201 if let Some(resolved_name) = resolved_name {
202 function_name = Some(resolved_name);
203 }
204 Some(FrameInfo::new(
205 module.module.name(),
206 func_index.index() as u32,
207 function_name,
208 func_start,
209 instr,
210 ))
211 }
212
213 pub fn lookup_trap_info(&self, pc: usize) -> Option<TrapInformation> {
215 let module = self.module_info(pc)?;
216 let func = module.function_info(pc)?;
217 let rel_pos = u32::try_from(pc - func.start).ok()?;
218
219 if let Some(debug_info) = module.function_debug_info(func.local_index) {
220 let traps = debug_info.traps();
221 let idx = traps
222 .binary_search_by_key(&rel_pos, |info| info.code_offset)
223 .ok()?;
224 return Some(traps[idx]);
225 }
226
227 module
229 .trap_reader
230 .as_ref()?
231 .lookup(func.local_index, rel_pos)
232 }
233
234 fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
236 let (end, module_info) = self.ranges.range(pc..).next()?;
237 if module_info.start <= pc && pc <= *end {
238 Some(module_info)
239 } else {
240 None
241 }
242 }
243}
244
245impl Drop for GlobalFrameInfoRegistration {
246 fn drop(&mut self) {
247 if let Ok(mut info) = FRAME_INFO.write() {
248 info.ranges.remove(&self.key);
249 }
250 }
251}
252
253#[derive(Debug)]
256#[repr(C)]
257pub struct FunctionExtent {
258 pub ptr: FunctionBodyPtr,
261 pub length: usize,
263}
264
265#[derive(Debug)]
268pub enum FrameInfosVariant {
269 Owned(PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>),
271 Archived(ArtifactBuildFromArchive),
273}
274
275impl FrameInfosVariant {
276 pub fn get(&self, index: LocalFunctionIndex) -> Option<CompiledFunctionFrameInfoVariant<'_>> {
278 match self {
279 Self::Owned(map) => map.get(index).map(CompiledFunctionFrameInfoVariant::Ref),
280 Self::Archived(archive) => archive
281 .get_frame_info_ref()
282 .expect("RKYV path expected")
283 .get(index)
284 .map(CompiledFunctionFrameInfoVariant::Archived),
285 }
286 }
287}
288
289#[derive(Debug)]
291pub enum CompiledFunctionFrameInfoVariant<'a> {
292 Ref(&'a CompiledFunctionFrameInfo),
294 Archived(&'a ArchivedCompiledFunctionFrameInfo),
296}
297
298impl CompiledFunctionFrameInfoVariant<'_> {
299 pub fn address_map(&self) -> FunctionAddressMapVariant<'_> {
301 match self {
302 CompiledFunctionFrameInfoVariant::Ref(info) => {
303 FunctionAddressMapVariant::Ref(&info.address_map)
304 }
305 CompiledFunctionFrameInfoVariant::Archived(info) => {
306 FunctionAddressMapVariant::Archived(&info.address_map)
307 }
308 }
309 }
310
311 pub fn traps(&self) -> VecTrapInformationVariant<'_> {
313 match self {
314 CompiledFunctionFrameInfoVariant::Ref(info) => {
315 VecTrapInformationVariant::Ref(&info.traps)
316 }
317 CompiledFunctionFrameInfoVariant::Archived(info) => {
318 let traps = rkyv::deserialize::<_, rkyv::rancor::Error>(&info.traps).unwrap();
319 VecTrapInformationVariant::Owned(traps)
320 }
321 }
322 }
323}
324
325#[derive(Debug)]
327pub enum VecTrapInformationVariant<'a> {
328 Ref(&'a Vec<TrapInformation>),
329 Owned(Vec<TrapInformation>),
330}
331
332impl Deref for VecTrapInformationVariant<'_> {
334 type Target = [TrapInformation];
335
336 fn deref(&self) -> &Self::Target {
337 match self {
338 VecTrapInformationVariant::Ref(traps) => traps,
339 VecTrapInformationVariant::Owned(traps) => traps,
340 }
341 }
342}
343
344#[derive(Debug)]
345pub enum FunctionAddressMapVariant<'a> {
346 Ref(&'a FunctionAddressMap),
347 Archived(&'a ArchivedFunctionAddressMap),
348}
349
350impl FunctionAddressMapVariant<'_> {
351 pub fn instructions(&self) -> FunctionAddressMapInstructionVariant<'_> {
352 match self {
353 FunctionAddressMapVariant::Ref(map) => {
354 FunctionAddressMapInstructionVariant::Owned(&map.instructions)
355 }
356 FunctionAddressMapVariant::Archived(map) => {
357 FunctionAddressMapInstructionVariant::Archived(&map.instructions)
358 }
359 }
360 }
361
362 pub fn start_srcloc(&self) -> SourceLoc {
363 match self {
364 FunctionAddressMapVariant::Ref(map) => map.start_srcloc,
365 FunctionAddressMapVariant::Archived(map) => {
366 rkyv::deserialize::<_, rkyv::rancor::Error>(&map.start_srcloc).unwrap()
367 }
368 }
369 }
370
371 pub fn end_srcloc(&self) -> SourceLoc {
372 match self {
373 FunctionAddressMapVariant::Ref(map) => map.end_srcloc,
374 FunctionAddressMapVariant::Archived(map) => {
375 rkyv::deserialize::<_, rkyv::rancor::Error>(&map.end_srcloc).unwrap()
376 }
377 }
378 }
379
380 pub fn body_offset(&self) -> usize {
381 match self {
382 FunctionAddressMapVariant::Ref(map) => map.body_offset,
383 FunctionAddressMapVariant::Archived(map) => map.body_offset.to_native() as usize,
384 }
385 }
386
387 pub fn body_len(&self) -> usize {
388 match self {
389 FunctionAddressMapVariant::Ref(map) => map.body_len,
390 FunctionAddressMapVariant::Archived(map) => map.body_len.to_native() as usize,
391 }
392 }
393}
394
395#[derive(Debug)]
396pub enum FunctionAddressMapInstructionVariant<'a> {
397 Owned(&'a Vec<InstructionAddressMap>),
398 Archived(&'a ArchivedVec<ArchivedInstructionAddressMap>),
399}
400
401impl FunctionAddressMapInstructionVariant<'_> {
402 pub fn code_offset_by_key(&self, key: usize) -> Result<usize, usize> {
403 match self {
404 FunctionAddressMapInstructionVariant::Owned(instructions) => {
405 instructions.binary_search_by_key(&key, |map| map.code_offset)
406 }
407 FunctionAddressMapInstructionVariant::Archived(instructions) => {
408 instructions.binary_search_by_key(&key, |map| map.code_offset.to_native() as usize)
409 }
410 }
411 }
412
413 pub fn get(&self, index: usize) -> InstructionAddressMap {
414 match self {
415 FunctionAddressMapInstructionVariant::Owned(instructions) => instructions[index],
416 FunctionAddressMapInstructionVariant::Archived(instructions) => instructions
417 .get(index)
418 .map(|map| InstructionAddressMap {
419 srcloc: rkyv::deserialize::<_, rkyv::rancor::Error>(&map.srcloc).unwrap(),
420 code_offset: map.code_offset.to_native() as usize,
421 code_len: map.code_len.to_native() as usize,
422 })
423 .unwrap(),
424 }
425 }
426}
427
428pub fn register(
435 module: Arc<ModuleInfo>,
436 finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
437 frame_infos: Option<FrameInfosVariant>,
438 image_base: usize,
439 elf_data: Option<Arc<[u8]>>,
440) -> Option<GlobalFrameInfoRegistration> {
441 register_with_source(
442 module,
443 finished_functions,
444 frame_infos,
445 image_base,
446 elf_data.map(DebugInfoSource::Bytes),
447 )
448}
449
450pub(crate) fn register_with_source(
451 module: Arc<ModuleInfo>,
452 finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
453 frame_infos: Option<FrameInfosVariant>,
454 image_base: usize,
455 elf_data: Option<DebugInfoSource>,
456) -> Option<GlobalFrameInfoRegistration> {
457 let trap_reader = elf_data.clone().map(TrapReader::new);
458 let mut min = usize::MAX;
459 let mut max = 0;
460 let mut functions = BTreeMap::new();
461 for (
462 i,
463 FunctionExtent {
464 ptr: start,
465 length: len,
466 },
467 ) in finished_functions.iter()
468 {
469 let start = **start as usize;
470 let end = start + len - 1;
472 min = cmp::min(min, start);
473 max = cmp::max(max, end);
474 let func = FunctionInfo {
475 start,
476 local_index: i,
477 };
478 assert!(functions.insert(end, func).is_none());
479 }
480 if functions.is_empty() {
481 return None;
482 }
483
484 let mut info = FRAME_INFO.write().unwrap();
485 if let Some((_, prev)) = info.ranges.range(max..).next() {
488 assert!(prev.start > max);
489 }
490 if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
491 assert!(*prev_end < min);
492 }
493
494 let prev = info.ranges.insert(
496 max,
497 ModuleInfoFrameInfo {
498 start: min,
499 functions,
500 module,
501 frame_infos,
502 image_base,
503 debug_info: DebugInfo::new(elf_data),
504 trap_reader,
505 },
506 );
507 assert!(prev.is_none());
508 Some(GlobalFrameInfoRegistration { key: max })
509}