@@ -40,20 +40,24 @@ lazy_static::lazy_static! {
40
40
static ref FRAME_RE : Regex = Regex :: new(
41
41
r#"(?xm)
42
42
^
43
- [\ ]*(?:\d+:)[\ ]* # leading frame number
43
+ \s*(?:\d+:)?\s* # frame number (missing for inline)
44
+
45
+ (?:
46
+ (?P<addr_old>0x[a-f0-9]+) # old style address prefix
47
+ \s-\s
48
+ )?
49
+
50
+ (?P<symbol>[^\r\n\(]+) # symbol name
51
+
44
52
(?:
45
- (?P<addr_oldsyntax>0x[a-f0-9]+) # addr
46
- [\ ]-[\ ]
47
- (?P<symbol_oldsyntax>[^\r\n]+)
48
- |
49
- (?P<symbol>[^\r\n]+)
50
- \((?P<addr>0x[a-f0-9]+)\) # addr
51
- )
53
+ \s\((?P<addr_new>0x[a-f0-9]+)\) # new style address in parens
54
+ )?
55
+
52
56
(?:
53
57
\r?\n
54
- [\ \t] +at[\ ]
55
- (?P<path>[^\r\n]+?)
56
- (?::(?P<lineno>\d+))?
58
+ \s +at\s # padded "at" in new line
59
+ (?P<path>[^\r\n]+?) # path to source file
60
+ (?::(?P<lineno>\d+))? # optional source line
57
61
)?
58
62
$
59
63
"#
@@ -62,31 +66,33 @@ lazy_static::lazy_static! {
62
66
}
63
67
64
68
fn parse_stacktrace ( bt : & str ) -> Option < Stacktrace > {
69
+ let mut last_address = None ;
70
+
65
71
let frames = FRAME_RE
66
72
. captures_iter ( & bt)
67
73
. map ( |captures| {
68
74
let abs_path = captures. name ( "path" ) . map ( |m| m. as_str ( ) . to_string ( ) ) ;
69
75
let filename = abs_path. as_ref ( ) . map ( |p| filename ( p) ) ;
70
- let real_symbol = captures
71
- . name ( "symbol" )
72
- . map_or_else ( || & captures[ "symbol_oldsyntax" ] , |m| m. as_str ( ) )
73
- . to_string ( ) ;
76
+ let real_symbol = captures[ "symbol" ] . to_string ( ) ;
74
77
let symbol = strip_symbol ( & real_symbol) ;
75
78
let function = demangle_symbol ( symbol) ;
79
+
80
+ // Obtain the instruction address. A missing address usually indicates an inlined stack
81
+ // frame, in which case the previous address needs to be used.
82
+ last_address = captures
83
+ . name ( "addr_new" )
84
+ . or_else ( || captures. name ( "addr_old" ) )
85
+ . and_then ( |m| m. as_str ( ) . parse ( ) . ok ( ) )
86
+ . or ( last_address) ;
87
+
76
88
Frame {
77
89
symbol : if symbol != function {
78
90
Some ( symbol. into ( ) )
79
91
} else {
80
92
None
81
93
} ,
82
94
function : Some ( function) ,
83
- instruction_addr : Some (
84
- captures
85
- . name ( "addr" )
86
- . map_or_else ( || & captures[ "addr_oldsyntax" ] , |m| m. as_str ( ) )
87
- . parse ( )
88
- . unwrap ( ) ,
89
- ) ,
95
+ instruction_addr : last_address,
90
96
abs_path,
91
97
filename,
92
98
lineno : captures
@@ -231,3 +237,41 @@ where
231
237
}
232
238
}
233
239
}
240
+
241
+ #[ test]
242
+ fn test_parse_stacktrace ( ) {
243
+ use crate :: protocol:: Addr ;
244
+
245
+ let backtrace = r#"
246
+ 2: <failure::error::error_impl::ErrorImpl as core::convert::From<F>>::from::h3bae66c036570137 (0x55a12174de62)
247
+ at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/failure-0.1.5/src/error/error_impl.rs:19
248
+ <failure::error::Error as core::convert::From<F>>::from::hc7d0d62dae166cea
249
+ at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/failure-0.1.5/src/error/mod.rs:36
250
+ failure::error_message::err_msg::he322d3ed9409189a
251
+ at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/failure-0.1.5/src/error_message.rs:12
252
+ rust::inline2::h562e5687710b6a71
253
+ at src/main.rs:5
254
+ rust::not_inline::h16f5b6019e5f0815
255
+ at src/main.rs:10
256
+ 7: main (0x55e3895a4dc7)
257
+ "# ;
258
+
259
+ let stacktrace = parse_stacktrace ( backtrace) . expect ( "stacktrace" ) ;
260
+ assert_eq ! ( stacktrace. frames. len( ) , 6 ) ;
261
+
262
+ assert_eq ! ( stacktrace. frames[ 0 ] . function, Some ( "main" . into( ) ) ) ;
263
+ assert_eq ! (
264
+ stacktrace. frames[ 0 ] . instruction_addr,
265
+ Some ( Addr ( 0x55e3_895a_4dc7 ) )
266
+ ) ;
267
+
268
+ // Inlined frame, inherits address from parent
269
+ assert_eq ! (
270
+ stacktrace. frames[ 1 ] . function,
271
+ Some ( "rust::not_inline" . into( ) )
272
+ ) ;
273
+ assert_eq ! (
274
+ stacktrace. frames[ 1 ] . instruction_addr,
275
+ Some ( Addr ( 0x55a1_2174_de62 ) )
276
+ ) ;
277
+ }
0 commit comments