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