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
37/// Parses a backtrace string into a Sentry `Stacktrace`.
38pub 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            // Obtain the instruction address. A missing address usually indicates an inlined stack
51            // frame, in which case the previous address needs to be used.
52            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}