pprof/
frames.rs

1// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3use std::borrow::Cow;
4use std::fmt::{self, Debug, Display, Formatter};
5use std::hash::{Hash, Hasher};
6use std::os::raw::c_void;
7use std::path::PathBuf;
8use std::time::SystemTime;
9
10use smallvec::SmallVec;
11use symbolic_demangle::demangle;
12
13use crate::backtrace::{Frame, Trace, TraceImpl};
14use crate::{MAX_DEPTH, MAX_THREAD_NAME};
15
16#[cfg(feature = "perfmaps")]
17fn resolve_in_perfmap(ip: usize) -> Option<Symbol> {
18    use crate::perfmap::get_resolver;
19
20    if let Some(perf_map_resolver) = get_resolver().as_ref() {
21        if let Some(symbol) = perf_map_resolver.find(ip as _) {
22            return Some(Symbol::from(symbol));
23        }
24    }
25
26    None
27}
28
29#[cfg(not(feature = "perfmaps"))]
30fn resolve_in_perfmap(_ip: usize) -> Option<Symbol> {
31    None
32}
33
34#[derive(Clone)]
35pub struct UnresolvedFrames {
36    pub frames: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]>,
37    pub thread_name: [u8; MAX_THREAD_NAME],
38    pub thread_name_length: usize,
39    pub thread_id: u64,
40    pub sample_timestamp: SystemTime,
41}
42
43impl Default for UnresolvedFrames {
44    fn default() -> Self {
45        let frames = SmallVec::with_capacity(MAX_DEPTH);
46        Self {
47            frames,
48            thread_name: [0; MAX_THREAD_NAME],
49            thread_name_length: 0,
50            thread_id: 0,
51            sample_timestamp: SystemTime::now(),
52        }
53    }
54}
55
56impl Debug for UnresolvedFrames {
57    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
58        self.frames.fmt(f)
59    }
60}
61
62impl UnresolvedFrames {
63    pub fn new(
64        frames: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]>,
65        tn: &[u8],
66        thread_id: u64,
67        sample_timestamp: SystemTime,
68    ) -> Self {
69        let thread_name_length = tn.len();
70        let mut thread_name = [0; MAX_THREAD_NAME];
71        thread_name[0..thread_name_length].clone_from_slice(tn);
72
73        Self {
74            frames,
75            thread_name,
76            thread_name_length,
77            thread_id,
78            sample_timestamp,
79        }
80    }
81}
82
83impl PartialEq for UnresolvedFrames {
84    fn eq(&self, other: &Self) -> bool {
85        let (frames1, frames2) = (&self.frames, &other.frames);
86        if self.thread_id != other.thread_id || frames1.len() != frames2.len() {
87            false
88        } else {
89            Iterator::zip(frames1.iter(), frames2.iter())
90                .all(|(s1, s2)| s1.symbol_address() == s2.symbol_address())
91        }
92    }
93}
94
95impl Eq for UnresolvedFrames {}
96
97impl Hash for UnresolvedFrames {
98    fn hash<H: Hasher>(&self, state: &mut H) {
99        self.frames
100            .iter()
101            .for_each(|frame| frame.symbol_address().hash(state));
102        self.thread_id.hash(state);
103    }
104}
105
106/// Symbol is a representation of a function symbol. It contains name and addr of it. If built with
107/// debug message, it can also provide line number and filename. The name in it is not demangled.
108#[derive(Debug, Clone)]
109pub struct Symbol {
110    /// This name is raw name of a symbol (which hasn't been demangled).
111    pub name: Option<Vec<u8>>,
112
113    /// The address of the function. It is not 100% trustworthy.
114    pub addr: Option<*mut c_void>,
115
116    /// Line number of this symbol. If compiled with debug message, you can get it.
117    pub lineno: Option<u32>,
118
119    /// Filename of this symbol. If compiled with debug message, you can get it.
120    pub filename: Option<PathBuf>,
121}
122
123impl Symbol {
124    pub fn raw_name(&self) -> &[u8] {
125        self.name.as_deref().unwrap_or(b"Unknown")
126    }
127
128    pub fn name(&self) -> String {
129        demangle(&String::from_utf8_lossy(self.raw_name())).into_owned()
130    }
131
132    pub fn sys_name(&self) -> Cow<str> {
133        String::from_utf8_lossy(self.raw_name())
134    }
135
136    pub fn filename(&self) -> Cow<str> {
137        self.filename
138            .as_ref()
139            .map(|name| name.as_os_str().to_string_lossy())
140            .unwrap_or_else(|| Cow::Borrowed("Unknown"))
141    }
142
143    pub fn lineno(&self) -> u32 {
144        self.lineno.unwrap_or(0)
145    }
146}
147
148unsafe impl Send for Symbol {}
149
150impl<T> From<&T> for Symbol
151where
152    T: crate::backtrace::Symbol,
153{
154    fn from(symbol: &T) -> Self {
155        Symbol {
156            name: symbol.name(),
157            addr: symbol.addr(),
158            lineno: symbol.lineno(),
159            filename: symbol.filename(),
160        }
161    }
162}
163
164impl Display for Symbol {
165    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
166        f.write_str(&self.name())
167    }
168}
169
170impl PartialEq for Symbol {
171    fn eq(&self, other: &Self) -> bool {
172        self.raw_name() == other.raw_name()
173    }
174}
175
176impl Hash for Symbol {
177    fn hash<H: Hasher>(&self, state: &mut H) {
178        self.raw_name().hash(state)
179    }
180}
181
182/// A representation of a backtrace. `thread_name` and `thread_id` was got from `pthread_getname_np`
183/// and `pthread_self`. frames is a vector of symbols.
184#[derive(Clone, PartialEq, Hash)]
185pub struct Frames {
186    pub frames: Vec<Vec<Symbol>>,
187    pub thread_name: String,
188    pub thread_id: u64,
189    pub sample_timestamp: SystemTime,
190}
191
192impl Frames {
193    /// Returns a thread identifier (name or ID) as a string.
194    pub fn thread_name_or_id(&self) -> String {
195        if !self.thread_name.is_empty() {
196            self.thread_name.clone()
197        } else {
198            format!("{:?}", self.thread_id)
199        }
200    }
201}
202
203impl From<UnresolvedFrames> for Frames {
204    fn from(frames: UnresolvedFrames) -> Self {
205        let mut fs = Vec::new();
206
207        let mut frame_iter = frames.frames.iter();
208
209        while let Some(frame) = frame_iter.next() {
210            let mut symbols: Vec<Symbol> = Vec::new();
211
212            if let Some(perfmap_symbol) = resolve_in_perfmap(frame.ip() as usize) {
213                symbols.push(perfmap_symbol);
214            } else {
215                frame.resolve_symbol(|symbol| {
216                    let symbol = Symbol::from(symbol);
217                    symbols.push(symbol);
218                });
219            }
220
221            if symbols.iter().any(|symbol| {
222                // macOS prepends an underscore even with `#[no_mangle]`
223                matches!(
224                    &*symbol.name(),
225                    "perf_signal_handler" | "_perf_signal_handler"
226                )
227            }) {
228                // ignore frame itself and its next one
229                frame_iter.next();
230                continue;
231            }
232
233            if !symbols.is_empty() {
234                fs.push(symbols);
235            }
236        }
237
238        Self {
239            frames: fs,
240            thread_name: String::from_utf8_lossy(&frames.thread_name[0..frames.thread_name_length])
241                .into_owned(),
242            thread_id: frames.thread_id,
243            sample_timestamp: frames.sample_timestamp,
244        }
245    }
246}
247
248impl Eq for Frames {}
249
250impl Debug for Frames {
251    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
252        for frame in self.frames.iter() {
253            write!(f, "FRAME: ")?;
254            for symbol in frame.iter() {
255                write!(f, "{} -> ", symbol)?;
256            }
257        }
258        write!(f, "THREAD: ")?;
259        if !self.thread_name.is_empty() {
260            write!(f, "{}", self.thread_name)
261        } else {
262            write!(f, "ThreadId({})", self.thread_id)
263        }
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270
271    #[test]
272    fn demangle_rust() {
273        let symbol = Symbol {
274            name: Some(b"_ZN3foo3barE".to_vec()),
275            addr: None,
276            lineno: None,
277            filename: None,
278        };
279
280        assert_eq!(&symbol.name(), "foo::bar")
281    }
282
283    #[test]
284    fn demangle_cpp() {
285        let name =
286            b"_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_"
287                .to_vec();
288
289        let symbol = Symbol {
290            name: Some(name),
291            addr: None,
292            lineno: None,
293            filename: None,
294        };
295
296        assert_eq!(
297            &symbol.name(),
298            "Map<StringName, Ref<GDScript>, Comparator<StringName>, DefaultAllocator>::has(StringName const&) const"
299        )
300    }
301}