use crate::serialize::SerializableFunctionFrameInfo;
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;
use wasmer_vm::{FunctionBodyPtr, ModuleInfo};
lazy_static::lazy_static! {
pub static ref FRAME_INFO: RwLock<GlobalFrameInfo> = Default::default();
}
#[derive(Default)]
pub struct GlobalFrameInfo {
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
}
pub struct GlobalFrameInfoRegistration {
key: usize,
}
struct ModuleInfoFrameInfo {
start: usize,
functions: BTreeMap<usize, FunctionInfo>,
module: Arc<ModuleInfo>,
frame_infos: PrimaryMap<LocalFunctionIndex, SerializableFunctionFrameInfo>,
}
impl ModuleInfoFrameInfo {
fn function_debug_info(
&self,
local_index: LocalFunctionIndex,
) -> &SerializableFunctionFrameInfo {
&self.frame_infos.get(local_index).unwrap()
}
fn process_function_debug_info(&mut self, local_index: LocalFunctionIndex) {
let func = self.frame_infos.get_mut(local_index).unwrap();
let processed: CompiledFunctionFrameInfo = match func {
SerializableFunctionFrameInfo::Processed(_) => {
return;
}
SerializableFunctionFrameInfo::Unprocessed(unprocessed) => unprocessed.deserialize(),
};
*func = SerializableFunctionFrameInfo::Processed(processed)
}
fn processed_function_frame_info(
&self,
local_index: LocalFunctionIndex,
) -> &CompiledFunctionFrameInfo {
match self.function_debug_info(local_index) {
SerializableFunctionFrameInfo::Processed(di) => &di,
_ => unreachable!("frame info should already be processed"),
}
}
fn function_info(&self, pc: usize) -> Option<&FunctionInfo> {
let (end, func) = self.functions.range(pc..).next()?;
if pc < func.start || *end < pc {
return None;
}
Some(func)
}
}
#[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
.processed_function_frame_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
}
}
};
debug_assert!(pos.is_some(), "failed to find instruction for {:x}", pc);
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.processed_function_frame_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])
}
pub fn should_process_frame(&self, pc: usize) -> Option<bool> {
let module = self.module_info(pc)?;
let func = module.function_info(pc)?;
let extra_func_info = module.function_debug_info(func.local_index);
Some(extra_func_info.is_unprocessed())
}
pub fn maybe_process_frame(&mut self, pc: usize) -> Option<()> {
let module = self.module_info_mut(pc)?;
let func = module.function_info(pc)?;
let func_local_index = func.local_index;
module.process_function_debug_info(func_local_index);
Some(())
}
fn module_info(&self, pc: usize) -> Option<&ModuleInfoFrameInfo> {
let (end, module_info) = self.ranges.range(pc..).next()?;
if pc < module_info.start || *end < pc {
return None;
}
Some(module_info)
}
fn module_info_mut(&mut self, pc: usize) -> Option<&mut ModuleInfoFrameInfo> {
let (end, module_info) = self.ranges.range_mut(pc..).next()?;
if pc < module_info.start || *end < pc {
return None;
}
Some(module_info)
}
}
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, SerializableFunctionFrameInfo>,
) -> 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
}
}