use std::collections::HashMap;
use crate::snapshot::modules::LoadedModule;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Frame {
pub function: String,
pub module: String,
pub relative_address: u64,
}
pub trait FrameResolver {
fn resolve(&mut self, address: u64) -> Frame;
}
#[derive(Debug)]
pub struct ModuleResolver {
modules: Vec<LoadedModule>,
cache: HashMap<u64, Frame>,
}
impl ModuleResolver {
pub fn new(modules: Vec<LoadedModule>) -> Self {
Self {
modules,
cache: HashMap::new(),
}
}
pub fn for_current_process() -> std::io::Result<Self> {
Ok(Self::new(crate::snapshot::modules::enumerate_modules()?))
}
pub fn module_count(&self) -> usize {
self.modules.len()
}
}
impl FrameResolver for ModuleResolver {
fn resolve(&mut self, address: u64) -> Frame {
if let Some(frame) = self.cache.get(&address) {
return frame.clone();
}
let frame = match crate::snapshot::modules::module_for_address(&self.modules, address) {
Some(module) => {
let name = module
.path
.as_deref()
.map(|path| {
std::path::Path::new(path)
.file_name()
.map(|leaf| leaf.to_string_lossy().into_owned())
.unwrap_or_else(|| path.to_string())
})
.unwrap_or_else(|| format!("<module@0x{:x}>", module.base));
let relative = address.saturating_sub(module.base);
Frame {
function: format!("{name}+0x{relative:x}"),
module: name,
relative_address: relative,
}
}
None => Frame {
function: format!("0x{address:x}"),
module: String::new(),
relative_address: address,
},
};
self.cache.insert(address, frame.clone());
frame
}
}
#[derive(Debug, Default)]
pub struct TableResolver {
names: HashMap<u64, String>,
}
impl TableResolver {
pub fn with(mut self, address: u64, name: &str) -> Self {
self.names.insert(address, name.to_string());
self
}
}
impl FrameResolver for TableResolver {
fn resolve(&mut self, address: u64) -> Frame {
Frame {
function: self
.names
.get(&address)
.cloned()
.unwrap_or_else(|| format!("0x{address:x}")),
module: String::new(),
relative_address: address,
}
}
}