1 use backtrace::BacktraceSymbol; 2 use std::fmt; 3 use std::hash::{Hash, Hasher}; 4 use std::ptr; 5 6 /// A symbol in a backtrace. 7 /// 8 /// This wrapper type serves two purposes. The first is that it provides a 9 /// representation of a symbol that can be inserted into hashmaps and hashsets; 10 /// the [`backtrace`] crate does not define [`Hash`], [`PartialEq`], or [`Eq`] 11 /// on [`BacktraceSymbol`], and recommends that users define their own wrapper 12 /// which implements these traits. 13 /// 14 /// Second, this wrapper includes a `parent_hash` field that uniquely 15 /// identifies this symbol's position in its trace. Otherwise, e.g., our code 16 /// would not be able to distinguish between recursive calls of a function at 17 /// different depths. 18 #[derive(Clone)] 19 pub(super) struct Symbol { 20 pub(super) symbol: BacktraceSymbol, 21 pub(super) parent_hash: u64, 22 } 23 24 impl Hash for Symbol { hash<H>(&self, state: &mut H) where H: Hasher,25 fn hash<H>(&self, state: &mut H) 26 where 27 H: Hasher, 28 { 29 if let Some(name) = self.symbol.name() { 30 name.as_bytes().hash(state); 31 } 32 33 if let Some(addr) = self.symbol.addr() { 34 ptr::hash(addr, state); 35 } 36 37 self.symbol.filename().hash(state); 38 self.symbol.lineno().hash(state); 39 self.symbol.colno().hash(state); 40 self.parent_hash.hash(state); 41 } 42 } 43 44 impl PartialEq for Symbol { eq(&self, other: &Self) -> bool45 fn eq(&self, other: &Self) -> bool { 46 (self.parent_hash == other.parent_hash) 47 && match (self.symbol.name(), other.symbol.name()) { 48 (None, None) => true, 49 (Some(lhs_name), Some(rhs_name)) => lhs_name.as_bytes() == rhs_name.as_bytes(), 50 _ => false, 51 } 52 && match (self.symbol.addr(), other.symbol.addr()) { 53 (None, None) => true, 54 (Some(lhs_addr), Some(rhs_addr)) => ptr::eq(lhs_addr, rhs_addr), 55 _ => false, 56 } 57 && (self.symbol.filename() == other.symbol.filename()) 58 && (self.symbol.lineno() == other.symbol.lineno()) 59 && (self.symbol.colno() == other.symbol.colno()) 60 } 61 } 62 63 impl Eq for Symbol {} 64 65 impl fmt::Display for Symbol { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 if let Some(name) = self.symbol.name() { 68 let name = name.to_string(); 69 let name = if let Some((name, _)) = name.rsplit_once("::") { 70 name 71 } else { 72 &name 73 }; 74 fmt::Display::fmt(&name, f)?; 75 } 76 77 if let Some(filename) = self.symbol.filename() { 78 f.write_str(" at ")?; 79 filename.to_string_lossy().fmt(f)?; 80 if let Some(lineno) = self.symbol.lineno() { 81 f.write_str(":")?; 82 fmt::Display::fmt(&lineno, f)?; 83 if let Some(colno) = self.symbol.colno() { 84 f.write_str(":")?; 85 fmt::Display::fmt(&colno, f)?; 86 } 87 } 88 } 89 90 Ok(()) 91 } 92 } 93