use std::cell::RefCell;
use addr2line::Context;
use object::read::File as ObjectFile;
use super::SymbolicatedFrame;
type Ctx = Context<addr2line::gimli::EndianRcSlice<addr2line::gimli::RunTimeEndian>>;
struct UnixSymbolicator {
ctx: Ctx,
}
thread_local! {
static SYMBOLICATOR: RefCell<Option<Option<UnixSymbolicator>>> =
const { RefCell::new(None) };
}
fn build() -> Option<UnixSymbolicator> {
let path = super::self_binary::self_exe()?;
let bytes = std::fs::read(path).ok()?;
let leaked: &'static [u8] = Box::leak(bytes.into_boxed_slice());
let obj = ObjectFile::parse(leaked).ok()?;
let ctx = Context::new(&obj).ok()?;
Some(UnixSymbolicator { ctx })
}
pub(crate) fn resolve(address: u64) -> Vec<SymbolicatedFrame> {
let unresolved = || {
vec![SymbolicatedFrame {
address,
function: None,
file: None,
line: None,
inlined: false,
}]
};
SYMBOLICATOR.with(|cell| {
let mut guard = match cell.try_borrow_mut() {
Ok(g) => g,
Err(_) => return unresolved(),
};
let sym = guard.get_or_insert_with(build);
let Some(sym) = sym.as_ref() else {
return unresolved();
};
let mut out = Vec::new();
let Ok(mut frames) = sym.ctx.find_frames(address).skip_all_loads() else {
return unresolved();
};
let mut idx = 0usize;
while let Ok(Some(frame)) = frames.next() {
let function = frame
.function
.as_ref()
.and_then(|f| f.raw_name().ok())
.map(|name| rustc_demangle::demangle(&name).to_string());
let (file, line) = match frame.location {
Some(loc) => (loc.file.map(std::path::PathBuf::from), loc.line),
None => (None, None),
};
out.push(SymbolicatedFrame {
address,
function,
file,
line,
inlined: idx > 0,
});
idx += 1;
}
if out.is_empty() {
out.push(SymbolicatedFrame {
address,
function: None,
file: None,
line: None,
inlined: false,
});
}
out
})
}