#![allow(clippy::from_over_into)]
#[cfg(feature = "serde")]
use crate::resolve;
use crate::PrintFmt;
use crate::{resolve_frame, trace, BacktraceFmt, Symbol, SymbolName};
use core::ffi::c_void;
use std::fmt;
use std::path::{Path, PathBuf};
use std::prelude::v1::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Backtrace {
frames: Box<[BacktraceFrame]>,
}
#[derive(Clone, Copy)]
struct TracePtr(*mut c_void);
unsafe impl Send for TracePtr {}
unsafe impl Sync for TracePtr {}
impl TracePtr {
fn into_void(self) -> *mut c_void {
self.0
}
#[cfg(feature = "serde")]
fn from_addr(addr: usize) -> Self {
TracePtr(addr as *mut c_void)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for TracePtr {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct PrimitiveVisitor;
impl<'de> serde::de::Visitor<'de> for PrimitiveVisitor {
type Value = TracePtr;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("usize")
}
#[inline]
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(TracePtr(v as usize as *mut c_void))
}
#[inline]
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(TracePtr(v as usize as *mut c_void))
}
#[inline]
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if usize::BITS >= 32 {
Ok(TracePtr(v as usize as *mut c_void))
} else {
Err(E::invalid_type(
serde::de::Unexpected::Unsigned(v as _),
&self,
))
}
}
#[inline]
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if usize::BITS >= 64 {
Ok(TracePtr(v as usize as *mut c_void))
} else {
Err(E::invalid_type(
serde::de::Unexpected::Unsigned(v as _),
&self,
))
}
}
}
deserializer.deserialize_u64(PrimitiveVisitor)
}
}
#[cfg(feature = "serde")]
impl Serialize for TracePtr {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_u64(self.0 as usize as u64)
}
}
fn _assert_send_sync() {
fn _assert<T: Send + Sync>() {}
_assert::<Backtrace>();
}
#[derive(Clone)]
pub struct BacktraceFrame {
frame: Frame,
symbols: Option<Box<[BacktraceSymbol]>>,
}
#[derive(Clone)]
enum Frame {
Raw(crate::Frame),
#[cfg(feature = "serde")]
Deserialized {
ip: TracePtr,
symbol_address: TracePtr,
module_base_address: Option<TracePtr>,
},
}
impl Frame {
fn ip(&self) -> *mut c_void {
match *self {
Frame::Raw(ref f) => f.ip(),
#[cfg(feature = "serde")]
Frame::Deserialized { ip, .. } => ip.into_void(),
}
}
fn symbol_address(&self) -> *mut c_void {
match *self {
Frame::Raw(ref f) => f.symbol_address(),
#[cfg(feature = "serde")]
Frame::Deserialized { symbol_address, .. } => symbol_address.into_void(),
}
}
fn module_base_address(&self) -> Option<*mut c_void> {
match *self {
Frame::Raw(ref f) => f.module_base_address(),
#[cfg(feature = "serde")]
Frame::Deserialized {
module_base_address,
..
} => module_base_address.map(|addr| addr.into_void()),
}
}
fn resolve_symbols(&self) -> Box<[BacktraceSymbol]> {
let mut symbols = Vec::new();
let sym = |symbol: &Symbol| {
symbols.push(BacktraceSymbol {
name: symbol.name().map(|m| m.as_bytes().into()),
addr: symbol.addr().map(TracePtr),
filename: symbol.filename().map(|m| m.to_owned()),
lineno: symbol.lineno(),
colno: symbol.colno(),
});
};
match *self {
Frame::Raw(ref f) => resolve_frame(f, sym),
#[cfg(feature = "serde")]
Frame::Deserialized { ip, .. } => {
resolve(ip.into_void(), sym);
}
}
symbols.into_boxed_slice()
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct BacktraceSymbol {
name: Option<Box<[u8]>>,
addr: Option<TracePtr>,
filename: Option<PathBuf>,
lineno: Option<u32>,
colno: Option<u32>,
}
impl Backtrace {
#[inline(never)] pub fn new() -> Backtrace {
let mut bt = Self::create(Self::new as usize);
bt.resolve();
bt
}
#[inline(never)] pub fn new_unresolved() -> Backtrace {
Self::create(Self::new_unresolved as usize)
}
fn create(ip: usize) -> Backtrace {
let mut frames = Vec::new();
trace(|frame| {
frames.push(BacktraceFrame {
frame: Frame::Raw(frame.clone()),
symbols: None,
});
if frame.symbol_address() as usize == ip {
frames.clear();
}
true
});
frames.shrink_to_fit();
Backtrace {
frames: frames.into_boxed_slice(),
}
}
pub fn frames(&self) -> &[BacktraceFrame] {
self.frames.as_ref()
}
pub fn resolve(&mut self) {
self.frames.iter_mut().for_each(BacktraceFrame::resolve);
}
}
impl From<Vec<BacktraceFrame>> for Backtrace {
fn from(frames: Vec<BacktraceFrame>) -> Self {
Backtrace {
frames: frames.into_boxed_slice(),
}
}
}
impl From<crate::Frame> for BacktraceFrame {
fn from(frame: crate::Frame) -> Self {
BacktraceFrame {
frame: Frame::Raw(frame),
symbols: None,
}
}
}
impl Into<Vec<BacktraceFrame>> for Backtrace {
fn into(self) -> Vec<BacktraceFrame> {
self.frames.into_vec()
}
}
impl BacktraceFrame {
pub fn ip(&self) -> *mut c_void {
self.frame.ip()
}
pub fn symbol_address(&self) -> *mut c_void {
self.frame.symbol_address()
}
pub fn module_base_address(&self) -> Option<*mut c_void> {
self.frame.module_base_address()
}
pub fn symbols(&self) -> &[BacktraceSymbol] {
self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[])
}
pub fn resolve(&mut self) {
if self.symbols.is_none() {
self.symbols = Some(self.frame.resolve_symbols());
}
}
}
impl BacktraceSymbol {
pub fn name(&self) -> Option<SymbolName<'_>> {
self.name.as_ref().map(|s| SymbolName::new(s))
}
pub fn addr(&self) -> Option<*mut c_void> {
self.addr.map(|s| s.into_void())
}
pub fn filename(&self) -> Option<&Path> {
self.filename.as_deref()
}
pub fn lineno(&self) -> Option<u32> {
self.lineno
}
pub fn colno(&self) -> Option<u32> {
self.colno
}
}
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let style = if fmt.alternate() {
PrintFmt::Full
} else {
PrintFmt::Short
};
let cwd = std::env::current_dir();
let mut print_path =
move |fmt: &mut fmt::Formatter<'_>, path: crate::BytesOrWideString<'_>| {
let path = path.into_path_buf();
if style != PrintFmt::Full {
if let Ok(cwd) = &cwd {
if let Ok(suffix) = path.strip_prefix(cwd) {
return fmt::Display::fmt(&suffix.display(), fmt);
}
}
}
fmt::Display::fmt(&path.display(), fmt)
};
let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
f.add_context()?;
for frame in &self.frames {
f.frame().backtrace_frame(frame)?;
}
f.finish()?;
Ok(())
}
}
impl Default for Backtrace {
fn default() -> Backtrace {
Backtrace::new()
}
}
impl fmt::Debug for BacktraceFrame {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("BacktraceFrame")
.field("ip", &self.ip())
.field("symbol_address", &self.symbol_address())
.finish()
}
}
impl fmt::Debug for BacktraceSymbol {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("BacktraceSymbol")
.field("name", &self.name())
.field("addr", &self.addr())
.field("filename", &self.filename())
.field("lineno", &self.lineno())
.field("colno", &self.colno())
.finish()
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::*;
use serde::de::Deserializer;
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct SerializedFrame {
ip: usize,
symbol_address: usize,
module_base_address: Option<usize>,
symbols: Option<Box<[BacktraceSymbol]>>,
}
impl Serialize for BacktraceFrame {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let BacktraceFrame { frame, symbols } = self;
SerializedFrame {
ip: frame.ip() as usize,
symbol_address: frame.symbol_address() as usize,
module_base_address: frame.module_base_address().map(|sym_a| sym_a as usize),
symbols: symbols.clone(),
}
.serialize(s)
}
}
impl<'a> Deserialize<'a> for BacktraceFrame {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
let frame: SerializedFrame = SerializedFrame::deserialize(d)?;
Ok(BacktraceFrame {
frame: Frame::Deserialized {
ip: TracePtr::from_addr(frame.ip),
symbol_address: TracePtr::from_addr(frame.symbol_address),
module_base_address: frame.module_base_address.map(TracePtr::from_addr),
},
symbols: frame.symbols,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_frame_conversion() {
let mut frames = vec![];
crate::trace(|frame| {
let converted = BacktraceFrame::from(frame.clone());
frames.push(converted);
true
});
let mut manual = Backtrace::from(frames);
manual.resolve();
let frames = manual.frames();
for frame in frames {
println!("{:?}", frame.ip());
println!("{:?}", frame.symbol_address());
println!("{:?}", frame.module_base_address());
println!("{:?}", frame.symbols());
}
}
}