use std::error::Error;
use addr2line::{
gimli::{EndianRcSlice, RunTimeEndian},
object::{read::File, Object},
Context,
};
pub(crate) struct Symbols<'sym> {
file: File<'sym, &'sym [u8]>,
ctx: Context<EndianRcSlice<RunTimeEndian>>,
}
impl<'sym> Symbols<'sym> {
pub fn try_from(bytes: &'sym [u8]) -> Result<Self, Box<dyn Error>> {
let file = File::parse(bytes)?;
let ctx = Context::new(&file)?;
Ok(Self { file, ctx })
}
pub fn get_name(&self, addr: u64) -> Option<String> {
self.ctx
.find_frames(addr)
.ok()
.and_then(|mut frames| {
frames.next().ok().flatten().and_then(|frame| {
frame
.function
.and_then(|name| name.demangle().map(|s| s.into_owned()).ok())
})
})
.or_else(|| {
self.file
.symbol_map()
.get(addr)
.map(|sym| sym.name().to_string())
})
}
pub fn get_location(&self, addr: u64) -> Option<(String, u32)> {
self.ctx.find_location(addr).ok()?.map(|location| {
let file = location.file.map(|f| f.to_string());
let line = location.line;
match (file, line) {
(Some(file), Some(line)) => Some((file, line)),
_ => None,
}
})?
}
}