sentry_backtrace/
parse.rs1use 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
38pub 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 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}