• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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