sentry_backtrace/
parse.rs

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