tokio/runtime/task/trace/
symbol.rs

1use backtrace::BacktraceSymbol;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4use 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)]
19pub(super) struct Symbol {
20    pub(super) symbol: BacktraceSymbol,
21    pub(super) parent_hash: u64,
22}
23
24impl Hash for Symbol {
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
44impl PartialEq for Symbol {
45    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
63impl Eq for Symbol {}
64
65impl fmt::Display for Symbol {
66    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}