use near_vm_compiler::{CompiledFunctionFrameInfo, SourceLoc, TrapInformation};
use near_vm_types::entity::{EntityRef, PrimaryMap};
use near_vm_types::{LocalFunctionIndex, ModuleInfo};
use parking_lot::RwLock;
use std::collections::BTreeMap;
use std::sync::Arc;
pub static FRAME_INFO: std::sync::LazyLock<RwLock<GlobalFrameInfo>> =
std::sync::LazyLock::new(|| Default::default());
#[derive(Default)]
pub struct GlobalFrameInfo {
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
}
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) {
FRAME_INFO.write().ranges.remove(&self.key);
}
}
#[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
}
}