#![allow(bad_style)]
extern crate backtrace_sys as bt;
use libc::uintptr_t;
use std::ffi::{CStr, OsStr};
use std::os::raw::{c_void, c_char, c_int};
use std::os::unix::prelude::*;
use std::path::Path;
use std::ptr;
use std::sync::{ONCE_INIT, Once};
use {Symbol, SymbolName};
type FileLine = (*const c_char, c_int);
extern fn error_cb(_data: *mut c_void, _msg: *const c_char,
_errnum: c_int) {
}
extern fn syminfo_cb(data: *mut c_void,
pc: uintptr_t,
symname: *const c_char,
_symval: uintptr_t,
_symsize: uintptr_t) {
struct SyminfoSymbol {
pc: uintptr_t,
symname: *const c_char,
}
impl Symbol for SyminfoSymbol {
fn name(&self) -> Option<SymbolName> {
if self.symname.is_null() {
None
} else {
Some(SymbolName::new(unsafe {
CStr::from_ptr(self.symname).to_bytes()
}))
}
}
fn addr(&self) -> Option<*mut c_void> {
if self.pc == 0 {None} else {Some(self.pc as *mut _)}
}
}
unsafe {
call(data, &SyminfoSymbol {
pc: pc,
symname: symname,
});
}
}
extern fn pcinfo_cb(data: *mut c_void,
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char) -> c_int {
struct PcinfoSymbol {
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
}
impl Symbol for PcinfoSymbol {
fn name(&self) -> Option<SymbolName> {
if self.function.is_null() {
None
} else {
Some(SymbolName::new(unsafe {
CStr::from_ptr(self.function).to_bytes()
}))
}
}
fn addr(&self) -> Option<*mut c_void> {
if self.pc == 0 {None} else {Some(self.pc as *mut _)}
}
fn filename(&self) -> Option<&Path> {
Some(Path::new(OsStr::from_bytes(unsafe {
CStr::from_ptr(self.filename).to_bytes()
})))
}
fn lineno(&self) -> Option<u32> {
Some(self.lineno as u32)
}
}
unsafe {
if filename.is_null() || function.is_null() {
return -1
}
call(data, &PcinfoSymbol {
pc: pc,
filename: filename,
lineno: lineno,
function: function,
});
return 0
}
}
unsafe fn call(data: *mut c_void, sym: &Symbol) {
let cb = data as *mut &mut FnMut(&Symbol);
let mut bomb = ::Bomb { enabled: true };
(*cb)(sym);
bomb.enabled = false;
}
unsafe fn init_state() -> *mut bt::backtrace_state {
static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
static INIT: Once = ONCE_INIT;
INIT.call_once(|| {
STATE = bt::backtrace_create_state(ptr::null(), 0, error_cb,
ptr::null_mut());
});
STATE
}
pub fn resolve(symaddr: *mut c_void, mut cb: &mut FnMut(&Symbol)) {
let _guard = ::lock::lock();
unsafe {
let state = init_state();
if state.is_null() {
return
}
let ret = bt::backtrace_pcinfo(state, symaddr as uintptr_t,
pcinfo_cb, error_cb,
&mut cb as *mut _ as *mut _);
if ret != 0 {
bt::backtrace_syminfo(state, symaddr as uintptr_t,
syminfo_cb, error_cb,
&mut cb as *mut _ as *mut _);
}
}
}