use backtrace::BacktraceSymbol; use std::fmt; use std::hash::{Hash, Hasher}; use std::ptr; /// A symbol in a backtrace. /// /// This wrapper type serves two purposes. The first is that it provides a /// representation of a symbol that can be inserted into hashmaps and hashsets; /// the [`backtrace`] crate does not define [`Hash`], [`PartialEq`], or [`Eq`] /// on [`BacktraceSymbol`], and recommends that users define their own wrapper /// which implements these traits. /// /// Second, this wrapper includes a `parent_hash` field that uniquely /// identifies this symbol's position in its trace. Otherwise, e.g., our code /// would not be able to distinguish between recursive calls of a function at /// different depths. #[derive(Clone)] pub(super) struct Symbol { pub(super) symbol: BacktraceSymbol, pub(super) parent_hash: u64, } impl Hash for Symbol { fn hash(&self, state: &mut H) where H: Hasher, { if let Some(name) = self.symbol.name() { name.as_bytes().hash(state); } if let Some(addr) = self.symbol.addr() { ptr::hash(addr, state); } self.symbol.filename().hash(state); self.symbol.lineno().hash(state); self.symbol.colno().hash(state); self.parent_hash.hash(state); } } impl PartialEq for Symbol { fn eq(&self, other: &Self) -> bool { (self.parent_hash == other.parent_hash) && match (self.symbol.name(), other.symbol.name()) { (None, None) => true, (Some(lhs_name), Some(rhs_name)) => lhs_name.as_bytes() == rhs_name.as_bytes(), _ => false, } && match (self.symbol.addr(), other.symbol.addr()) { (None, None) => true, (Some(lhs_addr), Some(rhs_addr)) => ptr::eq(lhs_addr, rhs_addr), _ => false, } && (self.symbol.filename() == other.symbol.filename()) && (self.symbol.lineno() == other.symbol.lineno()) && (self.symbol.colno() == other.symbol.colno()) } } impl Eq for Symbol {} impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(name) = self.symbol.name() { let name = name.to_string(); let name = if let Some((name, _)) = name.rsplit_once("::") { name } else { &name }; fmt::Display::fmt(&name, f)?; } if let Some(filename) = self.symbol.filename() { f.write_str(" at ")?; filename.to_string_lossy().fmt(f)?; if let Some(lineno) = self.symbol.lineno() { f.write_str(":")?; fmt::Display::fmt(&lineno, f)?; if let Some(colno) = self.symbol.colno() { f.write_str(":")?; fmt::Display::fmt(&colno, f)?; } } } Ok(()) } }