/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 | | } |