sentry_backtrace/
parse.rs
1use once_cell::sync::Lazy;
2use regex::Regex;
3
4use crate::utils::{demangle_symbol, filename, strip_symbol};
5use crate::{Frame, Stacktrace};
6
7static FRAME_RE: Lazy<Regex> = Lazy::new(|| {
8 Regex::new(
9 r#"(?xm)
10 ^
11 \s*(?:\d+:)?\s* # frame number (missing for inline)
12
13 (?:
14 (?P<addr_old>0x[a-f0-9]+) # old style address prefix
15 \s-\s
16 )?
17
18 (?P<symbol>[^\r\n\(]+) # symbol name
19
20 (?:
21 \s\((?P<addr_new>0x[a-f0-9]+)\) # new style address in parens
22 )?
23
24 (?:
25 \r?\n
26 \s+at\s # padded "at" in new line
27 (?P<path>[^\r\n]+?) # path to source file
28 (?::(?P<lineno>\d+))? # optional source line
29 (?::(?P<colno>\d+))? # optional source column
30 )?
31 $
32 "#,
33 )
34 .unwrap()
35});
36
37pub fn parse_stacktrace(bt: &str) -> Option<Stacktrace> {
39 let mut last_address = None;
40
41 let frames = FRAME_RE
42 .captures_iter(bt)
43 .map(|captures| {
44 let abs_path = captures.name("path").map(|m| m.as_str().to_string());
45 let filename = abs_path.as_ref().map(|p| filename(p).to_string());
46 let real_symbol = captures["symbol"].to_string();
47 let symbol = strip_symbol(&real_symbol);
48 let function = demangle_symbol(&symbol);
49
50 last_address = captures
53 .name("addr_new")
54 .or_else(|| captures.name("addr_old"))
55 .and_then(|m| m.as_str().parse().ok())
56 .or(last_address);
57
58 Frame {
59 symbol: if symbol != function {
60 Some(symbol.into())
61 } else {
62 None
63 },
64 function: Some(function),
65 instruction_addr: last_address,
66 abs_path,
67 filename,
68 lineno: captures
69 .name("lineno")
70 .map(|x| x.as_str().parse::<u64>().unwrap()),
71 colno: captures
72 .name("colno")
73 .map(|x| x.as_str().parse::<u64>().unwrap()),
74 ..Default::default()
75 }
76 })
77 .collect();
78
79 Stacktrace::from_frames_reversed(frames)
80}