Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pprof-0.15.0/src/frames.rs
Line
Count
Source
1
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2
3
use std::borrow::Cow;
4
use std::fmt::{self, Debug, Display, Formatter};
5
use std::hash::{Hash, Hasher};
6
use std::os::raw::c_void;
7
use std::path::PathBuf;
8
use std::time::SystemTime;
9
10
use smallvec::SmallVec;
11
use symbolic_demangle::demangle;
12
13
use crate::backtrace::{Frame, Trace, TraceImpl};
14
use crate::{MAX_DEPTH, MAX_THREAD_NAME};
15
16
#[cfg(feature = "perfmaps")]
17
fn 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"))]
30
0
fn resolve_in_perfmap(_ip: usize) -> Option<Symbol> {
31
0
    None
32
0
}
33
34
#[derive(Clone)]
35
pub 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
43
impl Default for UnresolvedFrames {
44
0
    fn default() -> Self {
45
0
        let frames = SmallVec::with_capacity(MAX_DEPTH);
46
0
        Self {
47
0
            frames,
48
0
            thread_name: [0; MAX_THREAD_NAME],
49
0
            thread_name_length: 0,
50
0
            thread_id: 0,
51
0
            sample_timestamp: SystemTime::now(),
52
0
        }
53
0
    }
54
}
55
56
impl Debug for UnresolvedFrames {
57
0
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
58
0
        self.frames.fmt(f)
59
0
    }
60
}
61
62
impl UnresolvedFrames {
63
0
    pub fn new(
64
0
        frames: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]>,
65
0
        tn: &[u8],
66
0
        thread_id: u64,
67
0
        sample_timestamp: SystemTime,
68
0
    ) -> Self {
69
0
        let thread_name_length = tn.len();
70
0
        let mut thread_name = [0; MAX_THREAD_NAME];
71
0
        thread_name[0..thread_name_length].clone_from_slice(tn);
72
73
0
        Self {
74
0
            frames,
75
0
            thread_name,
76
0
            thread_name_length,
77
0
            thread_id,
78
0
            sample_timestamp,
79
0
        }
80
0
    }
81
}
82
83
impl PartialEq for UnresolvedFrames {
84
0
    fn eq(&self, other: &Self) -> bool {
85
0
        let (frames1, frames2) = (&self.frames, &other.frames);
86
0
        if self.thread_id != other.thread_id || frames1.len() != frames2.len() {
87
0
            false
88
        } else {
89
0
            Iterator::zip(frames1.iter(), frames2.iter())
90
0
                .all(|(s1, s2)| s1.symbol_address() == s2.symbol_address())
91
        }
92
0
    }
93
}
94
95
impl Eq for UnresolvedFrames {}
96
97
impl Hash for UnresolvedFrames {
98
0
    fn hash<H: Hasher>(&self, state: &mut H) {
99
0
        self.frames
100
0
            .iter()
101
0
            .for_each(|frame| frame.symbol_address().hash(state));
102
0
        self.thread_id.hash(state);
103
0
    }
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)]
109
pub 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
123
impl Symbol {
124
0
    pub fn raw_name(&self) -> &[u8] {
125
0
        self.name.as_deref().unwrap_or(b"Unknown")
126
0
    }
127
128
0
    pub fn name(&self) -> String {
129
0
        demangle(&String::from_utf8_lossy(self.raw_name())).into_owned()
130
0
    }
131
132
0
    pub fn sys_name(&self) -> Cow<str> {
133
0
        String::from_utf8_lossy(self.raw_name())
134
0
    }
135
136
0
    pub fn filename(&self) -> Cow<str> {
137
0
        self.filename
138
0
            .as_ref()
139
0
            .map(|name| name.as_os_str().to_string_lossy())
140
0
            .unwrap_or_else(|| Cow::Borrowed("Unknown"))
141
0
    }
142
143
0
    pub fn lineno(&self) -> u32 {
144
0
        self.lineno.unwrap_or(0)
145
0
    }
146
}
147
148
unsafe impl Send for Symbol {}
149
150
impl<T> From<&T> for Symbol
151
where
152
    T: crate::backtrace::Symbol,
153
{
154
0
    fn from(symbol: &T) -> Self {
155
0
        Symbol {
156
0
            name: symbol.name(),
157
0
            addr: symbol.addr(),
158
0
            lineno: symbol.lineno(),
159
0
            filename: symbol.filename(),
160
0
        }
161
0
    }
162
}
163
164
impl Display for Symbol {
165
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
166
0
        f.write_str(&self.name())
167
0
    }
168
}
169
170
impl PartialEq for Symbol {
171
0
    fn eq(&self, other: &Self) -> bool {
172
0
        self.raw_name() == other.raw_name()
173
0
    }
174
}
175
176
impl Hash for Symbol {
177
0
    fn hash<H: Hasher>(&self, state: &mut H) {
178
0
        self.raw_name().hash(state)
179
0
    }
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)]
185
pub 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
192
impl Frames {
193
    /// Returns a thread identifier (name or ID) as a string.
194
0
    pub fn thread_name_or_id(&self) -> String {
195
0
        if !self.thread_name.is_empty() {
196
0
            self.thread_name.clone()
197
        } else {
198
0
            format!("{:?}", self.thread_id)
199
        }
200
0
    }
201
}
202
203
impl From<UnresolvedFrames> for Frames {
204
0
    fn from(frames: UnresolvedFrames) -> Self {
205
0
        let mut fs = Vec::new();
206
207
0
        let mut frame_iter = frames.frames.iter();
208
209
0
        while let Some(frame) = frame_iter.next() {
210
0
            let mut symbols: Vec<Symbol> = Vec::new();
211
212
0
            if let Some(perfmap_symbol) = resolve_in_perfmap(frame.ip() as usize) {
213
0
                symbols.push(perfmap_symbol);
214
0
            } else {
215
0
                frame.resolve_symbol(|symbol| {
216
0
                    let symbol = Symbol::from(symbol);
217
0
                    symbols.push(symbol);
218
0
                });
219
            }
220
221
0
            if symbols.iter().any(|symbol| {
222
                // macOS prepends an underscore even with `#[no_mangle]`
223
0
                matches!(
224
0
                    &*symbol.name(),
225
0
                    "perf_signal_handler" | "_perf_signal_handler"
226
                )
227
0
            }) {
228
                // ignore frame itself and its next one
229
0
                frame_iter.next();
230
0
                continue;
231
0
            }
232
233
0
            if !symbols.is_empty() {
234
0
                fs.push(symbols);
235
0
            }
236
        }
237
238
0
        Self {
239
0
            frames: fs,
240
0
            thread_name: String::from_utf8_lossy(&frames.thread_name[0..frames.thread_name_length])
241
0
                .into_owned(),
242
0
            thread_id: frames.thread_id,
243
0
            sample_timestamp: frames.sample_timestamp,
244
0
        }
245
0
    }
246
}
247
248
impl Eq for Frames {}
249
250
impl Debug for Frames {
251
0
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
252
0
        for frame in self.frames.iter() {
253
0
            write!(f, "FRAME: ")?;
254
0
            for symbol in frame.iter() {
255
0
                write!(f, "{} -> ", symbol)?;
256
            }
257
        }
258
0
        write!(f, "THREAD: ")?;
259
0
        if !self.thread_name.is_empty() {
260
0
            write!(f, "{}", self.thread_name)
261
        } else {
262
0
            write!(f, "ThreadId({})", self.thread_id)
263
        }
264
0
    }
265
}
266
267
#[cfg(test)]
268
mod 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
}