mirror of
https://git.proxmox.com/git/rustc
synced 2025-06-10 21:53:07 +00:00
618 lines
19 KiB
Rust
618 lines
19 KiB
Rust
#![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};
|
|
|
|
/// Representation of an owned and self-contained backtrace.
|
|
///
|
|
/// This structure can be used to capture a backtrace at various points in a
|
|
/// program and later used to inspect what the backtrace was at that time.
|
|
///
|
|
/// `Backtrace` supports pretty-printing of backtraces through its `Debug`
|
|
/// implementation.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
|
pub struct Backtrace {
|
|
// Frames here are listed from top-to-bottom of the stack
|
|
frames: Vec<BacktraceFrame>,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct TracePtr(*mut c_void);
|
|
/// SAFETY: These pointers are always valid within a process and are not used for mutation.
|
|
unsafe impl Send for TracePtr {}
|
|
/// SAFETY: These pointers are always valid within a process and are not used for mutation.
|
|
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>();
|
|
}
|
|
|
|
/// Captured version of a frame in a backtrace.
|
|
///
|
|
/// This type is returned as a list from `Backtrace::frames` and represents one
|
|
/// stack frame in a captured backtrace.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
#[derive(Clone)]
|
|
pub struct BacktraceFrame {
|
|
frame: Frame,
|
|
symbols: Option<Vec<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()),
|
|
}
|
|
}
|
|
|
|
/// Resolve all addresses in the frame to their symbolic names.
|
|
fn resolve_symbols(&self) -> Vec<BacktraceSymbol> {
|
|
let mut symbols = Vec::new();
|
|
let sym = |symbol: &Symbol| {
|
|
symbols.push(BacktraceSymbol {
|
|
name: symbol.name().map(|m| m.as_bytes().to_vec()),
|
|
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
|
|
}
|
|
}
|
|
|
|
/// Captured version of a symbol in a backtrace.
|
|
///
|
|
/// This type is returned as a list from `BacktraceFrame::symbols` and
|
|
/// represents the metadata for a symbol in a backtrace.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
#[derive(Clone)]
|
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
|
pub struct BacktraceSymbol {
|
|
name: Option<Vec<u8>>,
|
|
addr: Option<TracePtr>,
|
|
filename: Option<PathBuf>,
|
|
lineno: Option<u32>,
|
|
colno: Option<u32>,
|
|
}
|
|
|
|
impl Backtrace {
|
|
/// Captures a backtrace at the callsite of this function, returning an
|
|
/// owned representation.
|
|
///
|
|
/// This function is useful for representing a backtrace as an object in
|
|
/// Rust. This returned value can be sent across threads and printed
|
|
/// elsewhere, and the purpose of this value is to be entirely self
|
|
/// contained.
|
|
///
|
|
/// Note that on some platforms acquiring a full backtrace and resolving it
|
|
/// can be extremely expensive. If the cost is too much for your application
|
|
/// it's recommended to instead use `Backtrace::new_unresolved()` which
|
|
/// avoids the symbol resolution step (which typically takes the longest)
|
|
/// and allows deferring that to a later date.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use backtrace::Backtrace;
|
|
///
|
|
/// let current_backtrace = Backtrace::new();
|
|
/// ```
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
#[inline(never)] // want to make sure there's a frame here to remove
|
|
pub fn new() -> Backtrace {
|
|
let mut bt = Self::create(Self::new as usize);
|
|
bt.resolve();
|
|
bt
|
|
}
|
|
|
|
/// Similar to `new` except that this does not resolve any symbols, this
|
|
/// simply captures the backtrace as a list of addresses.
|
|
///
|
|
/// At a later time the `resolve` function can be called to resolve this
|
|
/// backtrace's symbols into readable names. This function exists because
|
|
/// the resolution process can sometimes take a significant amount of time
|
|
/// whereas any one backtrace may only be rarely printed.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use backtrace::Backtrace;
|
|
///
|
|
/// let mut current_backtrace = Backtrace::new_unresolved();
|
|
/// println!("{current_backtrace:?}"); // no symbol names
|
|
/// current_backtrace.resolve();
|
|
/// println!("{current_backtrace:?}"); // symbol names now present
|
|
/// ```
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
#[inline(never)] // want to make sure there's a frame here to remove
|
|
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,
|
|
});
|
|
|
|
// clear inner frames, and start with call site.
|
|
if frame.symbol_address() as usize == ip {
|
|
frames.clear();
|
|
}
|
|
|
|
true
|
|
});
|
|
frames.shrink_to_fit();
|
|
|
|
Backtrace { frames }
|
|
}
|
|
|
|
/// Returns the frames from when this backtrace was captured.
|
|
///
|
|
/// The first entry of this slice is likely the function `Backtrace::new`,
|
|
/// and the last frame is likely something about how this thread or the main
|
|
/// function started.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn frames(&self) -> &[BacktraceFrame] {
|
|
self.frames.as_slice()
|
|
}
|
|
|
|
/// If this backtrace was created from `new_unresolved` then this function
|
|
/// will resolve all addresses in the backtrace to their symbolic names.
|
|
///
|
|
/// If this backtrace has been previously resolved or was created through
|
|
/// `new`, this function does nothing.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
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 }
|
|
}
|
|
}
|
|
|
|
impl From<crate::Frame> for BacktraceFrame {
|
|
fn from(frame: crate::Frame) -> Self {
|
|
BacktraceFrame {
|
|
frame: Frame::Raw(frame),
|
|
symbols: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
// we don't want to implement `impl From<Backtrace> for Vec<BacktraceFrame>` on purpose,
|
|
// because "... additional directions for Vec<T> can weaken type inference ..."
|
|
// more information on https://github.com/rust-lang/backtrace-rs/pull/526
|
|
impl Into<Vec<BacktraceFrame>> for Backtrace {
|
|
fn into(self) -> Vec<BacktraceFrame> {
|
|
self.frames
|
|
}
|
|
}
|
|
|
|
impl BacktraceFrame {
|
|
/// Same as `Frame::ip`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn ip(&self) -> *mut c_void {
|
|
self.frame.ip()
|
|
}
|
|
|
|
/// Same as `Frame::symbol_address`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn symbol_address(&self) -> *mut c_void {
|
|
self.frame.symbol_address()
|
|
}
|
|
|
|
/// Same as `Frame::module_base_address`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn module_base_address(&self) -> Option<*mut c_void> {
|
|
self.frame.module_base_address()
|
|
}
|
|
|
|
/// Returns the list of symbols that this frame corresponds to.
|
|
///
|
|
/// Normally there is only one symbol per frame, but sometimes if a number
|
|
/// of functions are inlined into one frame then multiple symbols will be
|
|
/// returned. The first symbol listed is the "innermost function", whereas
|
|
/// the last symbol is the outermost (last caller).
|
|
///
|
|
/// Note that if this frame came from an unresolved backtrace then this will
|
|
/// return an empty list.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn symbols(&self) -> &[BacktraceSymbol] {
|
|
self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[])
|
|
}
|
|
|
|
/// Resolve all addresses in this frame to their symbolic names.
|
|
///
|
|
/// If this frame has been previously resolved, this function does nothing.
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn resolve(&mut self) {
|
|
if self.symbols.is_none() {
|
|
self.symbols = Some(self.frame.resolve_symbols());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BacktraceSymbol {
|
|
/// Same as `Symbol::name`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn name(&self) -> Option<SymbolName<'_>> {
|
|
self.name.as_ref().map(|s| SymbolName::new(s))
|
|
}
|
|
|
|
/// Same as `Symbol::addr`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn addr(&self) -> Option<*mut c_void> {
|
|
self.addr.map(|s| s.into_void())
|
|
}
|
|
|
|
/// Same as `Symbol::filename`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn filename(&self) -> Option<&Path> {
|
|
self.filename.as_deref()
|
|
}
|
|
|
|
/// Same as `Symbol::lineno`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
pub fn lineno(&self) -> Option<u32> {
|
|
self.lineno
|
|
}
|
|
|
|
/// Same as `Symbol::colno`
|
|
///
|
|
/// # Required features
|
|
///
|
|
/// This function requires the `std` feature of the `backtrace` crate to be
|
|
/// enabled, and the `std` feature is enabled by default.
|
|
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
|
|
};
|
|
|
|
// When printing paths we try to strip the cwd if it exists, otherwise
|
|
// we just print the path as-is. Note that we also only do this for the
|
|
// short format, because if it's full we presumably want to print
|
|
// everything.
|
|
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<Vec<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());
|
|
}
|
|
}
|
|
}
|