use core::{fmt, str};
cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
use std::path::Path;
use std::prelude::v1::*;
}
}
use crate::backtrace::Frame;
use crate::types::BytesOrWideString;
use core::ffi::c_void;
use rustc_demangle::{try_demangle, Demangle};
#[cfg(feature = "std")]
pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) {
let _guard = crate::lock::lock();
unsafe { resolve_unsynchronized(addr, cb) }
}
#[cfg(feature = "std")]
pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) {
let _guard = crate::lock::lock();
unsafe { resolve_frame_unsynchronized(frame, cb) }
}
pub enum ResolveWhat<'a> {
Address(*mut c_void),
Frame(&'a Frame),
}
impl<'a> ResolveWhat<'a> {
#[allow(dead_code)]
fn address_or_ip(&self) -> *mut c_void {
match self {
ResolveWhat::Address(a) => adjust_ip(*a),
ResolveWhat::Frame(f) => adjust_ip(f.ip()),
}
}
}
fn adjust_ip(a: *mut c_void) -> *mut c_void {
if a.is_null() {
a
} else {
(a as usize - 1) as *mut c_void
}
}
pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F)
where
F: FnMut(&Symbol),
{
resolve_imp(ResolveWhat::Address(addr), &mut cb)
}
pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F)
where
F: FnMut(&Symbol),
{
resolve_imp(ResolveWhat::Frame(frame), &mut cb)
}
pub struct Symbol {
inner: SymbolImp<'static>,
}
impl Symbol {
pub fn name(&self) -> Option<SymbolName<'_>> {
self.inner.name()
}
pub fn addr(&self) -> Option<*mut c_void> {
self.inner.addr().map(|p| p as *mut _)
}
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
self.inner.filename_raw()
}
pub fn lineno(&self) -> Option<u32> {
self.inner.lineno()
}
#[cfg(feature = "std")]
#[allow(unreachable_code)]
pub fn filename(&self) -> Option<&Path> {
self.inner.filename()
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Symbol");
if let Some(name) = self.name() {
d.field("name", &name);
}
if let Some(addr) = self.addr() {
d.field("addr", &addr);
}
#[cfg(feature = "std")]
{
if let Some(filename) = self.filename() {
d.field("filename", &filename);
}
}
if let Some(lineno) = self.lineno() {
d.field("lineno", &lineno);
}
d.finish()
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "cpp_demangle")] {
struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
impl<'a> OptionCppSymbol<'a> {
fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok())
}
fn none() -> OptionCppSymbol<'a> {
OptionCppSymbol(None)
}
}
} else {
use core::marker::PhantomData;
struct OptionCppSymbol<'a>(PhantomData<&'a ()>);
impl<'a> OptionCppSymbol<'a> {
fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> {
OptionCppSymbol(PhantomData)
}
fn none() -> OptionCppSymbol<'a> {
OptionCppSymbol(PhantomData)
}
}
}
}
#[allow(dead_code)]
pub struct SymbolName<'a> {
bytes: &'a [u8],
demangled: Option<Demangle<'a>>,
cpp_demangled: OptionCppSymbol<'a>,
}
impl<'a> SymbolName<'a> {
pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
let str_bytes = str::from_utf8(bytes).ok();
let demangled = str_bytes.and_then(|s| try_demangle(s).ok());
let cpp = if demangled.is_none() {
OptionCppSymbol::parse(bytes)
} else {
OptionCppSymbol::none()
};
SymbolName {
bytes: bytes,
demangled: demangled,
cpp_demangled: cpp,
}
}
pub fn as_str(&self) -> Option<&'a str> {
self.demangled
.as_ref()
.map(|s| s.as_str())
.or_else(|| str::from_utf8(self.bytes).ok())
}
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
fn format_symbol_name(
fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result,
mut bytes: &[u8],
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
while bytes.len() > 0 {
match str::from_utf8(bytes) {
Ok(name) => {
fmt(name, f)?;
break;
}
Err(err) => {
fmt("\u{FFFD}", f)?;
match err.error_len() {
Some(len) => bytes = &bytes[err.valid_up_to() + len..],
None => break,
}
}
}
}
Ok(())
}
cfg_if::cfg_if! {
if #[cfg(feature = "cpp_demangle")] {
impl<'a> fmt::Display for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else if let Some(ref cpp) = self.cpp_demangled.0 {
cpp.fmt(f)
} else {
format_symbol_name(fmt::Display::fmt, self.bytes, f)
}
}
}
} else {
impl<'a> fmt::Display for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else {
format_symbol_name(fmt::Display::fmt, self.bytes, f)
}
}
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(feature = "std", feature = "cpp_demangle"))] {
impl<'a> fmt::Debug for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::fmt::Write;
if let Some(ref s) = self.demangled {
return s.fmt(f)
}
if let Some(ref cpp) = self.cpp_demangled.0 {
let mut s = String::new();
if write!(s, "{}", cpp).is_ok() {
return s.fmt(f)
}
}
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
}
}
} else {
impl<'a> fmt::Debug for SymbolName<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref s) = self.demangled {
s.fmt(f)
} else {
format_symbol_name(fmt::Debug::fmt, self.bytes, f)
}
}
}
}
}
#[cfg(feature = "std")]
pub fn clear_symbol_cache() {
let _guard = crate::lock::lock();
unsafe {
clear_symbol_cache_imp();
}
}
cfg_if::cfg_if! {
if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] {
mod dbghelp;
use self::dbghelp::resolve as resolve_imp;
use self::dbghelp::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(
feature = "libbacktrace",
any(unix, all(windows, not(target_vendor = "uwp"), target_env = "gnu")),
not(target_os = "fuchsia"),
not(target_os = "emscripten"),
not(target_env = "uclibc"),
))] {
mod libbacktrace;
use self::libbacktrace::resolve as resolve_imp;
use self::libbacktrace::Symbol as SymbolImp;
unsafe fn clear_symbol_cache_imp() {}
} else if #[cfg(all(
feature = "gimli-symbolize",
any(unix, windows),
not(target_os = "emscripten"),
))] {
mod gimli;
use self::gimli::resolve as resolve_imp;
use self::gimli::Symbol as SymbolImp;
use self::gimli::clear_symbol_cache as clear_symbol_cache_imp;
} else {
mod noop;
use self::noop::resolve as resolve_imp;
use self::noop::Symbol as SymbolImp;
#[allow(unused)]
unsafe fn clear_symbol_cache_imp() {}
}
}