1use 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#[derive(Clone)]
17pub struct UnresolvedFrames {
18 pub frames: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]>,
19 pub thread_name: [u8; MAX_THREAD_NAME],
20 pub thread_name_length: usize,
21 pub thread_id: u64,
22 pub sample_timestamp: SystemTime,
23}
24
25impl Default for UnresolvedFrames {
26 fn default() -> Self {
27 let frames = SmallVec::with_capacity(MAX_DEPTH);
28 Self {
29 frames,
30 thread_name: [0; MAX_THREAD_NAME],
31 thread_name_length: 0,
32 thread_id: 0,
33 sample_timestamp: SystemTime::now(),
34 }
35 }
36}
37
38impl Debug for UnresolvedFrames {
39 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
40 self.frames.fmt(f)
41 }
42}
43
44impl UnresolvedFrames {
45 pub fn new(
46 frames: SmallVec<[<TraceImpl as Trace>::Frame; MAX_DEPTH]>,
47 tn: &[u8],
48 thread_id: u64,
49 sample_timestamp: SystemTime,
50 ) -> Self {
51 let thread_name_length = tn.len();
52 let mut thread_name = [0; MAX_THREAD_NAME];
53 thread_name[0..thread_name_length].clone_from_slice(tn);
54
55 Self {
56 frames,
57 thread_name,
58 thread_name_length,
59 thread_id,
60 sample_timestamp,
61 }
62 }
63}
64
65impl PartialEq for UnresolvedFrames {
66 fn eq(&self, other: &Self) -> bool {
67 let (frames1, frames2) = (&self.frames, &other.frames);
68 if self.thread_id != other.thread_id || frames1.len() != frames2.len() {
69 false
70 } else {
71 Iterator::zip(frames1.iter(), frames2.iter())
72 .map(|(s1, s2)| s1.symbol_address() == s2.symbol_address())
73 .all(|equal| equal)
74 }
75 }
76}
77
78impl Eq for UnresolvedFrames {}
79
80impl Hash for UnresolvedFrames {
81 fn hash<H: Hasher>(&self, state: &mut H) {
82 self.frames
83 .iter()
84 .for_each(|frame| frame.symbol_address().hash(state));
85 self.thread_id.hash(state);
86 }
87}
88
89#[derive(Debug, Clone)]
92pub struct Symbol {
93 pub name: Option<Vec<u8>>,
95
96 pub addr: Option<*mut c_void>,
98
99 pub lineno: Option<u32>,
101
102 pub filename: Option<PathBuf>,
104}
105
106impl Symbol {
107 pub fn raw_name(&self) -> &[u8] {
108 self.name.as_deref().unwrap_or(b"Unknown")
109 }
110
111 pub fn name(&self) -> String {
112 demangle(&String::from_utf8_lossy(self.raw_name())).into_owned()
113 }
114
115 pub fn sys_name(&self) -> Cow<str> {
116 String::from_utf8_lossy(self.raw_name())
117 }
118
119 pub fn filename(&self) -> Cow<str> {
120 self.filename
121 .as_ref()
122 .map(|name| name.as_os_str().to_string_lossy())
123 .unwrap_or_else(|| Cow::Borrowed("Unknown"))
124 }
125
126 pub fn lineno(&self) -> u32 {
127 self.lineno.unwrap_or(0)
128 }
129}
130
131unsafe impl Send for Symbol {}
132
133impl<T> From<&T> for Symbol
134where
135 T: crate::backtrace::Symbol,
136{
137 fn from(symbol: &T) -> Self {
138 Symbol {
139 name: symbol.name(),
140 addr: symbol.addr(),
141 lineno: symbol.lineno(),
142 filename: symbol.filename(),
143 }
144 }
145}
146
147impl Display for Symbol {
148 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
149 f.write_str(&self.name())
150 }
151}
152
153impl PartialEq for Symbol {
154 fn eq(&self, other: &Self) -> bool {
155 self.raw_name() == other.raw_name()
156 }
157}
158
159impl Hash for Symbol {
160 fn hash<H: Hasher>(&self, state: &mut H) {
161 self.raw_name().hash(state)
162 }
163}
164
165#[derive(Clone, PartialEq, Hash)]
168pub struct Frames {
169 pub frames: Vec<Vec<Symbol>>,
170 pub thread_name: String,
171 pub thread_id: u64,
172 pub sample_timestamp: SystemTime,
173}
174
175impl Frames {
176 pub fn thread_name_or_id(&self) -> String {
178 if !self.thread_name.is_empty() {
179 self.thread_name.clone()
180 } else {
181 format!("{:?}", self.thread_id)
182 }
183 }
184}
185
186impl From<UnresolvedFrames> for Frames {
187 fn from(frames: UnresolvedFrames) -> Self {
188 let mut fs = Vec::new();
189
190 let mut frame_iter = frames.frames.iter();
191
192 while let Some(frame) = frame_iter.next() {
193 let mut symbols: Vec<Symbol> = Vec::new();
194
195 frame.resolve_symbol(|symbol| {
196 let symbol = Symbol::from(symbol);
197 symbols.push(symbol);
198 });
199
200 if symbols.iter().any(|symbol| {
201 matches!(
203 &*symbol.name(),
204 "perf_signal_handler" | "_perf_signal_handler"
205 )
206 }) {
207 frame_iter.next();
209 continue;
210 }
211
212 if !symbols.is_empty() {
213 fs.push(symbols);
214 }
215 }
216
217 Self {
218 frames: fs,
219 thread_name: String::from_utf8_lossy(&frames.thread_name[0..frames.thread_name_length])
220 .into_owned(),
221 thread_id: frames.thread_id,
222 sample_timestamp: frames.sample_timestamp,
223 }
224 }
225}
226
227impl Eq for Frames {}
228
229impl Debug for Frames {
230 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
231 for frame in self.frames.iter() {
232 write!(f, "FRAME: ")?;
233 for symbol in frame.iter() {
234 write!(f, "{} -> ", symbol)?;
235 }
236 }
237 write!(f, "THREAD: ")?;
238 if !self.thread_name.is_empty() {
239 write!(f, "{}", self.thread_name)
240 } else {
241 write!(f, "ThreadId({})", self.thread_id)
242 }
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn demangle_rust() {
252 let symbol = Symbol {
253 name: Some(b"_ZN3foo3barE".to_vec()),
254 addr: None,
255 lineno: None,
256 filename: None,
257 };
258
259 assert_eq!(&symbol.name(), "foo::bar")
260 }
261
262 #[test]
263 fn demangle_cpp() {
264 let name =
265 b"_ZNK3MapI10StringName3RefI8GDScriptE10ComparatorIS0_E16DefaultAllocatorE3hasERKS0_"
266 .to_vec();
267
268 let symbol = Symbol {
269 name: Some(name),
270 addr: None,
271 lineno: None,
272 filename: None,
273 };
274
275 assert_eq!(
276 &symbol.name(),
277 "Map<StringName, Ref<GDScript>, Comparator<StringName>, DefaultAllocator>::has(StringName const&) const"
278 )
279 }
280}