use loupe::MemoryUsage;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};
use wasmer_compiler::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation};
use wasmer_types::entity::{BoxedSlice, EntityRef, PrimaryMap};
use wasmer_types::{LocalFunctionIndex, ModuleInfo};
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>,
}
#[derive(MemoryUsage)]
pub struct GlobalFrameInfoRegistration {
key: usize,
}
#[derive(Debug)]
struct ModuleInfoFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<ModuleInfo>,
frame_infos: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
}
impl ModuleInfoFrameInfo {
fn function_debug_info(&self, local_index: LocalFunctionIndex) -> &CompiledFunctionFrameInfo {
&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 {
return 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 instr_map = &module.function_debug_info(func.local_index).address_map;
let pos = match instr_map
.instructions
.binary_search_by_key(&rel_pos, |map| map.code_offset)
{
Ok(pos) => Some(pos),
Err(0) => None,
Err(n) => {
let instr = &instr_map.instructions[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[pos].srcloc,
None => instr_map.start_srcloc,
};
let func_index = module.module.func_index(func.local_index);
Some(FrameInfo {
module_name: module.module.name(),
func_index: func_index.index() as u32,
function_name: module.module.function_names.get(&func_index).cloned(),
instr,
func_start: instr_map.start_srcloc,
})
}
pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let traps = &module.function_debug_info(func.local_index).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,
}
pub fn register(
module: Arc<ModuleInfo>,
finished_functions: &BoxedSlice<LocalFunctionIndex, FunctionExtent>,
frame_infos: PrimaryMap<LocalFunctionIndex, CompiledFunctionFrameInfo>,
) -> Option<GlobalFrameInfoRegistration> {
let mut min = usize::max_value();
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;
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 })
}
#[derive(Debug, Clone)]
pub struct FrameInfo {
module_name: String,
func_index: u32,
function_name: Option<String>,
func_start: SourceLoc,
instr: SourceLoc,
}
impl FrameInfo {
pub fn func_index(&self) -> u32 {
self.func_index
}
pub fn module_name(&self) -> &str {
&self.module_name
}
pub fn function_name(&self) -> Option<&str> {
self.function_name.as_deref()
}
pub fn module_offset(&self) -> usize {
self.instr.bits() as usize
}
pub fn func_offset(&self) -> usize {
(self.instr.bits() - self.func_start.bits()) as usize
}
}