use addr2line;
use addr2line::object::{self, Object};
use findshlibs::{self, Segment, SharedLibrary};
use libc::c_void;
use memmap::Mmap;
use std::cell::RefCell;
use std::env;
use std::fs::File;
use std::mem;
use std::path::{Path, PathBuf};
use std::prelude::v1::*;
use std::u32;
use symbolize::ResolveWhat;
use types::BytesOrWideString;
use SymbolName;
const MAPPINGS_CACHE_SIZE: usize = 4;
type Dwarf = addr2line::Context;
type Symbols<'map> = object::SymbolMap<'map>;
struct Mapping {
dwarf: Dwarf,
symbols: Symbols<'static>,
_map: Mmap,
}
impl Mapping {
fn new(path: &PathBuf) -> Option<Mapping> {
let file = File::open(path).ok()?;
let map = unsafe { Mmap::map(&file).ok()? };
let (dwarf, symbols) = {
let object = object::ElfFile::parse(&*map).ok()?;
let dwarf = addr2line::Context::new(&object).ok()?;
let symbols = object.symbol_map();
(dwarf, unsafe { mem::transmute(symbols) })
};
Some(Mapping {
dwarf,
symbols,
_map: map,
})
}
fn rent<F>(&self, mut f: F)
where
F: FnMut(&Dwarf, &Symbols),
{
f(&self.dwarf, &self.symbols)
}
}
thread_local! {
static MAPPINGS_CACHE: RefCell<Vec<(PathBuf, Mapping)>>
= RefCell::new(Vec::with_capacity(MAPPINGS_CACHE_SIZE));
}
fn with_mapping_for_path<F>(path: PathBuf, f: F)
where
F: FnMut(&Dwarf, &Symbols),
{
MAPPINGS_CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
let idx = cache.iter().position(|&(ref p, _)| p == &path);
if let Some(idx) = idx {
if idx != 0 {
let entry = cache.remove(idx);
cache.insert(0, entry);
}
} else {
let mapping = match Mapping::new(&path) {
None => return,
Some(m) => m,
};
if cache.len() == MAPPINGS_CACHE_SIZE {
cache.pop();
}
cache.insert(0, (path, mapping));
}
cache[0].1.rent(f);
});
}
pub fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
let addr = what.address_or_ip();
let addr = findshlibs::Avma(addr as *mut u8 as *const u8);
let mut so_info = None;
findshlibs::TargetSharedLibrary::each(|so| {
use findshlibs::IterationControl::*;
for segment in so.segments() {
if segment.contains_avma(so, addr) {
let addr = so.avma_to_svma(addr);
let path = so.name().to_string_lossy();
so_info = Some((addr, path.to_string()));
return Break;
}
}
Continue
});
let (addr, path) = match so_info {
None => return,
Some((a, p)) => (a, p),
};
let path = if path.is_empty() {
match env::current_exe() {
Err(_) => return,
Ok(p) => p,
}
} else {
PathBuf::from(path)
};
with_mapping_for_path(path, |dwarf, symbols| {
let mut found_sym = false;
if let Ok(mut frames) = dwarf.find_frames(addr.0 as u64) {
while let Ok(Some(frame)) = frames.next() {
let (file, line) = frame
.location
.map(|l| (l.file, l.line))
.unwrap_or((None, None));
let name = frame
.function
.and_then(|f| f.raw_name().ok().map(|f| f.to_string()));
let sym = super::Symbol {
inner: Symbol::new(addr.0 as usize, file, line, name),
};
cb(&sym);
found_sym = true;
}
}
if !found_sym {
if let Some(name) = symbols.get(addr.0 as u64).and_then(|x| x.name()) {
let sym = super::Symbol {
inner: Symbol::new(addr.0 as usize, None, None, Some(name.to_string())),
};
cb(&sym);
}
}
});
}
pub struct Symbol {
addr: usize,
file: Option<String>,
line: Option<u64>,
name: Option<String>,
}
impl Symbol {
fn new(addr: usize, file: Option<String>, line: Option<u64>, name: Option<String>) -> Symbol {
Symbol {
addr,
file,
line,
name,
}
}
pub fn name(&self) -> Option<SymbolName> {
self.name.as_ref().map(|s| SymbolName::new(s.as_bytes()))
}
pub fn addr(&self) -> Option<*mut c_void> {
Some(self.addr as *mut c_void)
}
pub fn filename_raw(&self) -> Option<BytesOrWideString> {
self.file
.as_ref()
.map(|f| BytesOrWideString::Bytes(f.as_bytes()))
}
pub fn filename(&self) -> Option<&Path> {
self.file.as_ref().map(Path::new)
}
pub fn lineno(&self) -> Option<u32> {
self.line.and_then(|l| {
if l > (u32::MAX as u64) {
None
} else {
Some(l as u32)
}
})
}
}