use crate::types::address_map::{
ArchivedFunctionAddressMap, ArchivedInstructionAddressMap, FunctionAddressMap,
InstructionAddressMap,
};
use crate::types::function::{ArchivedCompiledFunctionFrameInfo, CompiledFunctionFrameInfo};
use crate::ArtifactBuildFromArchive;
use rkyv::vec::ArchivedVec;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use wasmer_types::lib::std::{cmp, ops::Deref};
use wasmer_types::{
entity::{BoxedSlice, EntityRef, PrimaryMap},
FrameInfo, LocalFunctionIndex, ModuleInfo, SourceLoc, TrapInformation,
};
use wasmer_vm::FunctionBodyPtr;
lazy_static::lazy_static! {
pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
}
#[derive(Default)]
pub struct GlobalFrameInfo {
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
}
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
pub struct GlobalFrameInfoRegistration {
key: usize,
}
#[derive(Debug)]
struct ModuleInfoFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<ModuleInfo>,
frame_infos: FrameInfosVariant,
}
impl ModuleInfoFrameInfo {
fn function_debug_info(
&self,
local_index: LocalFunctionIndex,
) -> CompiledFunctionFrameInfoVariant {
self.frame_infos.get(local_index).unwrap()
}
fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
let (end, func) = self.functions.range(pc..).next()?;
if func.start <= pc && pc <= *end {
Some(func)
} else {
None
}
}
}
#[derive(Debug)]
struct FunctionInfo {
start: usize,
local_index: LocalFunctionIndex,
}
impl GlobalFrameInfo {
pub fn lookup_frame_info(&self, pc: usize) -> Option<FrameInfo> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let rel_pos = pc - func.start;
let debug_info = module.function_debug_info(func.local_index);
let instr_map = debug_info.address_map();
let pos = match instr_map.instructions().code_offset_by_key(rel_pos) {
Ok(pos) => Some(pos),
Err(0) => None,
Err(n) => {
let instr = &instr_map.instructions().get(n - 1);
if instr.code_offset <= rel_pos && rel_pos < instr.code_offset + instr.code_len {
Some(n - 1)
} else {
None
}
}
};
let instr = match pos {
Some(pos) => instr_map.instructions().get(pos).srcloc,
None => instr_map.start_srcloc(),
};
let func_index = module.module.func_index(func.local_index);
Some(FrameInfo::new(
module.module.name(),
func_index.index() as u32,
module.module.function_names.get(&func_index).cloned(),
instr_map.start_srcloc(),
instr,
))
}
pub fn lookup_trap_info(&self, pc: usize) -> Option<TrapInformation> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let debug_info = module.function_debug_info(func.local_index);
let traps = debug_info.traps();
let idx = traps
.binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset)
.ok()?;
Some(traps[idx])
}
fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
let (end, module_info) = self.ranges.range(pc..).next()?;
if module_info.start <= pc && pc <= *end {
Some(module_info)
} else {
None
}
}
}
impl Drop for GlobalFrameInfoRegistration {
fn drop(&mut self) {
if let Ok(mut info) = FRAME_INFO.write() {
info.ranges.remove(&self.key);
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct FunctionExtent {
pub ptr: FunctionBodyPtr,
pub length: usize,
}
#[derive(Debug)]
pub enum FrameInfosVariant {
Owned(PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>),
Archived(ArtifactBuildFromArchive),
}
impl FrameInfosVariant {
pub fn get(&self, index: LocalFunctionIndex) -> Option<CompiledFunctionFrameInfoVariant> {
match self {
Self::Owned(map) => map.get(index).map(CompiledFunctionFrameInfoVariant::Ref),
Self::Archived(archive) => archive
.get_frame_info_ref()
.get(index)
.map(CompiledFunctionFrameInfoVariant::Archived),
}
}
}
#[derive(Debug)]
pub enum CompiledFunctionFrameInfoVariant<'a> {
Ref(&'a CompiledFunctionFrameInfo),
Archived(&'a ArchivedCompiledFunctionFrameInfo),
}
impl CompiledFunctionFrameInfoVariant<'_> {
pub fn address_map(&self) -> FunctionAddressMapVariant<'_> {
match self {
CompiledFunctionFrameInfoVariant::Ref(info) => {
FunctionAddressMapVariant::Ref(&info.address_map)
}
CompiledFunctionFrameInfoVariant::Archived(info) => {
FunctionAddressMapVariant::Archived(&info.address_map)
}
}
}
pub fn traps(&self) -> VecTrapInformationVariant {
match self {
CompiledFunctionFrameInfoVariant::Ref(info) => {
VecTrapInformationVariant::Ref(&info.traps)
}
CompiledFunctionFrameInfoVariant::Archived(info) => {
let traps = rkyv::deserialize::<_, rkyv::rancor::Error>(&info.traps).unwrap();
VecTrapInformationVariant::Owned(traps)
}
}
}
}
#[derive(Debug)]
pub enum VecTrapInformationVariant<'a> {
Ref(&'a Vec<TrapInformation>),
Owned(Vec<TrapInformation>),
}
impl Deref for VecTrapInformationVariant<'_> {
type Target = [TrapInformation];
fn deref(&self) -> &Self::Target {
match self {
VecTrapInformationVariant::Ref(traps) => traps,
VecTrapInformationVariant::Owned(traps) => traps,
}
}
}
#[derive(Debug)]
pub enum FunctionAddressMapVariant<'a> {
Ref(&'a FunctionAddressMap),
Archived(&'a ArchivedFunctionAddressMap),
}
impl FunctionAddressMapVariant<'_> {
pub fn instructions(&self) -> FunctionAddressMapInstructionVariant {
match self {
FunctionAddressMapVariant::Ref(map) => {
FunctionAddressMapInstructionVariant::Owned(&map.instructions)
}
FunctionAddressMapVariant::Archived(map) => {
FunctionAddressMapInstructionVariant::Archived(&map.instructions)
}
}
}
pub fn start_srcloc(&self) -> SourceLoc {
match self {
FunctionAddressMapVariant::Ref(map) => map.start_srcloc,
FunctionAddressMapVariant::Archived(map) => {
rkyv::deserialize::<_, rkyv::rancor::Error>(&map.start_srcloc).unwrap()
}
}
}
pub fn end_srcloc(&self) -> SourceLoc {
match self {
FunctionAddressMapVariant::Ref(map) => map.end_srcloc,
FunctionAddressMapVariant::Archived(map) => {
rkyv::deserialize::<_, rkyv::rancor::Error>(&map.end_srcloc).unwrap()
}
}
}
pub fn body_offset(&self) -> usize {
match self {
FunctionAddressMapVariant::Ref(map) => map.body_offset,
FunctionAddressMapVariant::Archived(map) => map.body_offset.to_native() as usize,
}
}
pub fn body_len(&self) -> usize {
match self {
FunctionAddressMapVariant::Ref(map) => map.body_len,
FunctionAddressMapVariant::Archived(map) => map.body_len.to_native() as usize,
}
}
}
#[derive(Debug)]
pub enum FunctionAddressMapInstructionVariant<'a> {
Owned(&'a Vec<InstructionAddressMap>),
Archived(&'a ArchivedVec<ArchivedInstructionAddressMap>),
}
impl FunctionAddressMapInstructionVariant<'_> {
pub fn code_offset_by_key(&self, key: usize) -> Result<usize, usize> {
match self {
FunctionAddressMapInstructionVariant::Owned(instructions) => {
instructions.binary_search_by_key(&key, |map| map.code_offset)
}
FunctionAddressMapInstructionVariant::Archived(instructions) => {
instructions.binary_search_by_key(&key, |map| map.code_offset.to_native() as usize)
}
}
}
pub fn get(&self, index: usize) -> InstructionAddressMap {
match self {
FunctionAddressMapInstructionVariant::Owned(instructions) => instructions[index],
FunctionAddressMapInstructionVariant::Archived(instructions) => instructions
.get(index)
.map(|map| InstructionAddressMap {
srcloc: rkyv::deserialize::<_, rkyv::rancor::Error>(&map.srcloc).unwrap(),
code_offset: map.code_offset.to_native() as usize,
code_len: map.code_len.to_native() as usize,
})
.unwrap(),
}
}
}
pub fn register(
module: Arc<ModuleInfo>,
finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
frame_infos: FrameInfosVariant,
) -> Option<GlobalFrameInfoRegistration> {
let mut min = usize::MAX;
let mut max = 0;
let mut functions = BTreeMap::new();
for (
i,
FunctionExtent {
ptr: start,
length: len,
},
) in finished_functions.iter()
{
let start = **start as usize;
let end = start + len - 1;
min = cmp::min(min, start);
max = cmp::max(max, end);
let func = FunctionInfo {
start,
local_index: i,
};
assert!(functions.insert(end, func).is_none());
}
if functions.is_empty() {
return None;
}
let mut info = FRAME_INFO.write().unwrap();
if let Some((_, prev)) = info.ranges.range(max..).next() {
assert!(prev.start > max);
}
if let Some((prev_end, _)) = info.ranges.range(..=min).next_back() {
assert!(*prev_end < min);
}
let prev = info.ranges.insert(
max,
ModuleInfoFrameInfo {
start: min,
functions,
module,
frame_infos,
},
);
assert!(prev.is_none());
Some(GlobalFrameInfoRegistration { key: max })
}