2
2
/// supported platforms.
3
3
4
4
use crate :: env;
5
+ use crate :: fmt;
5
6
use crate :: io;
6
7
use crate :: io:: prelude:: * ;
7
- use crate :: mem;
8
8
use crate :: path:: { self , Path } ;
9
- use crate :: ptr;
10
9
use crate :: sync:: atomic:: { self , Ordering } ;
11
10
use crate :: sys:: mutex:: Mutex ;
12
11
13
- use backtrace:: { BytesOrWideString , Frame , Symbol } ;
14
-
15
- pub const HEX_WIDTH : usize = 2 + 2 * mem:: size_of :: < usize > ( ) ;
12
+ use backtrace:: { BacktraceFmt , BytesOrWideString , PrintFmt } ;
16
13
17
14
/// Max number of frames to print.
18
15
const MAX_NB_FRAMES : usize = 100 ;
19
16
20
17
/// Prints the current backtrace.
21
- pub fn print ( w : & mut dyn Write , format : PrintFormat ) -> io:: Result < ( ) > {
18
+ pub fn print ( w : & mut dyn Write , format : PrintFmt ) -> io:: Result < ( ) > {
22
19
static LOCK : Mutex = Mutex :: new ( ) ;
23
20
24
21
// There are issues currently linking libbacktrace into tests, and in
@@ -39,26 +36,66 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> {
39
36
}
40
37
}
41
38
42
- fn _print ( w : & mut dyn Write , format : PrintFormat ) -> io:: Result < ( ) > {
43
- writeln ! ( w, "stack backtrace:" ) ?;
39
+ fn _print ( w : & mut dyn Write , format : PrintFmt ) -> io:: Result < ( ) > {
40
+ struct DisplayBacktrace {
41
+ format : PrintFmt ,
42
+ }
43
+ impl fmt:: Display for DisplayBacktrace {
44
+ fn fmt ( & self , fmt : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
45
+ _print_fmt ( fmt, self . format )
46
+ }
47
+ }
48
+ write ! ( w, "{}" , DisplayBacktrace { format } )
49
+ }
44
50
45
- let mut printer = Printer :: new ( format, w) ;
51
+ fn _print_fmt ( fmt : & mut fmt:: Formatter < ' _ > , print_fmt : PrintFmt ) -> fmt:: Result {
52
+ let mut print_path = move |fmt : & mut fmt:: Formatter < ' _ > , bows : BytesOrWideString < ' _ > | {
53
+ output_filename ( fmt, bows, print_fmt)
54
+ } ;
55
+ let mut bt_fmt = BacktraceFmt :: new ( fmt, print_fmt, & mut print_path) ;
56
+ bt_fmt. add_context ( ) ?;
57
+ let mut skipped = false ;
46
58
unsafe {
59
+ let mut idx = 0 ;
60
+ let mut res = Ok ( ( ) ) ;
47
61
backtrace:: trace_unsynchronized ( |frame| {
62
+ if print_fmt == PrintFmt :: Short && idx > MAX_NB_FRAMES {
63
+ skipped = true ;
64
+ return false ;
65
+ }
66
+
48
67
let mut hit = false ;
68
+ let mut stop = false ;
49
69
backtrace:: resolve_frame_unsynchronized ( frame, |symbol| {
50
70
hit = true ;
51
- printer. output ( frame, Some ( symbol) ) ;
71
+ if print_fmt == PrintFmt :: Short {
72
+ if let Some ( sym) = symbol. name ( ) . and_then ( |s| s. as_str ( ) ) {
73
+ if sym. contains ( "__rust_begin_short_backtrace" ) {
74
+ skipped = true ;
75
+ stop = true ;
76
+ return ;
77
+ }
78
+ }
79
+ }
80
+
81
+ res = bt_fmt. frame ( ) . symbol ( frame, symbol) ;
52
82
} ) ;
83
+ if stop {
84
+ return false ;
85
+ }
53
86
if !hit {
54
- printer . output ( frame, None ) ;
87
+ res = bt_fmt . frame ( ) . print_raw ( frame. ip ( ) , None , None , None ) ;
55
88
}
56
- !printer. done
89
+
90
+ idx += 1 ;
91
+ res. is_ok ( )
57
92
} ) ;
93
+ res?;
58
94
}
59
- if printer. skipped {
95
+ bt_fmt. finish ( ) ?;
96
+ if skipped {
60
97
writeln ! (
61
- w ,
98
+ fmt ,
62
99
"note: Some details are omitted, \
63
100
run with `RUST_BACKTRACE=full` for a verbose backtrace."
64
101
) ?;
@@ -77,33 +114,24 @@ where
77
114
f ( )
78
115
}
79
116
80
- /// Controls how the backtrace should be formatted.
81
- #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
82
- pub enum PrintFormat {
83
- /// Show only relevant data from the backtrace.
84
- Short = 2 ,
85
- /// Show all the frames with absolute path for files.
86
- Full = 3 ,
87
- }
88
-
89
117
// For now logging is turned off by default, and this function checks to see
90
118
// whether the magical environment variable is present to see if it's turned on.
91
- pub fn log_enabled ( ) -> Option < PrintFormat > {
119
+ pub fn log_enabled ( ) -> Option < PrintFmt > {
92
120
static ENABLED : atomic:: AtomicIsize = atomic:: AtomicIsize :: new ( 0 ) ;
93
121
match ENABLED . load ( Ordering :: SeqCst ) {
94
122
0 => { }
95
123
1 => return None ,
96
- 2 => return Some ( PrintFormat :: Short ) ,
97
- _ => return Some ( PrintFormat :: Full ) ,
124
+ 2 => return Some ( PrintFmt :: Short ) ,
125
+ _ => return Some ( PrintFmt :: Full ) ,
98
126
}
99
127
100
128
let val = env:: var_os ( "RUST_BACKTRACE" ) . and_then ( |x| {
101
129
if & x == "0" {
102
130
None
103
131
} else if & x == "full" {
104
- Some ( PrintFormat :: Full )
132
+ Some ( PrintFmt :: Full )
105
133
} else {
106
- Some ( PrintFormat :: Short )
134
+ Some ( PrintFmt :: Short )
107
135
}
108
136
} ) ;
109
137
ENABLED . store (
@@ -116,130 +144,45 @@ pub fn log_enabled() -> Option<PrintFormat> {
116
144
val
117
145
}
118
146
119
- struct Printer < ' a , ' b > {
120
- format : PrintFormat ,
121
- done : bool ,
122
- skipped : bool ,
123
- idx : usize ,
124
- out : & ' a mut ( dyn Write + ' b ) ,
125
- }
126
-
127
- impl < ' a , ' b > Printer < ' a , ' b > {
128
- fn new ( format : PrintFormat , out : & ' a mut ( dyn Write + ' b ) ) -> Printer < ' a , ' b > {
129
- Printer { format, done : false , skipped : false , idx : 0 , out }
130
- }
131
-
132
- /// Prints the symbol of the backtrace frame.
133
- ///
134
- /// These output functions should now be used everywhere to ensure consistency.
135
- /// You may want to also use `output_fileline`.
136
- fn output ( & mut self , frame : & Frame , symbol : Option < & Symbol > ) {
137
- if self . idx > MAX_NB_FRAMES {
138
- self . done = true ;
139
- self . skipped = true ;
140
- return ;
141
- }
142
- if self . _output ( frame, symbol) . is_err ( ) {
143
- self . done = true ;
144
- }
145
- self . idx += 1 ;
146
- }
147
-
148
- fn _output ( & mut self , frame : & Frame , symbol : Option < & Symbol > ) -> io:: Result < ( ) > {
149
- if self . format == PrintFormat :: Short {
150
- if let Some ( sym) = symbol. and_then ( |s| s. name ( ) ) . and_then ( |s| s. as_str ( ) ) {
151
- if sym. contains ( "__rust_begin_short_backtrace" ) {
152
- self . skipped = true ;
153
- self . done = true ;
154
- return Ok ( ( ) ) ;
155
- }
156
- }
157
-
158
- // Remove the `17: 0x0 - <unknown>` line.
159
- if self . format == PrintFormat :: Short && frame. ip ( ) == ptr:: null_mut ( ) {
160
- self . skipped = true ;
161
- return Ok ( ( ) ) ;
162
- }
163
- }
164
-
165
- match self . format {
166
- PrintFormat :: Full => {
167
- write ! ( self . out, " {:2}: {:2$?} - " , self . idx, frame. ip( ) , HEX_WIDTH ) ?
168
- }
169
- PrintFormat :: Short => write ! ( self . out, " {:2}: " , self . idx) ?,
170
- }
171
-
172
- match symbol. and_then ( |s| s. name ( ) ) {
173
- Some ( symbol) => {
174
- match self . format {
175
- PrintFormat :: Full => write ! ( self . out, "{}" , symbol) ?,
176
- // Strip the trailing hash if short mode.
177
- PrintFormat :: Short => write ! ( self . out, "{:#}" , symbol) ?,
178
- }
179
- }
180
- None => self . out . write_all ( b"<unknown>" ) ?,
147
+ /// Prints the filename of the backtrace frame.
148
+ ///
149
+ /// See also `output`.
150
+ fn output_filename (
151
+ fmt : & mut fmt:: Formatter < ' _ > ,
152
+ bows : BytesOrWideString < ' _ > ,
153
+ print_fmt : PrintFmt ,
154
+ ) -> fmt:: Result {
155
+ #[ cfg( windows) ]
156
+ let path_buf;
157
+ let file = match bows {
158
+ #[ cfg( unix) ]
159
+ BytesOrWideString :: Bytes ( bytes) => {
160
+ use crate :: os:: unix:: prelude:: * ;
161
+ Path :: new ( crate :: ffi:: OsStr :: from_bytes ( bytes) )
181
162
}
182
- self . out . write_all ( b" \n " ) ? ;
183
- if let Some ( sym ) = symbol {
184
- self . output_fileline ( sym ) ? ;
163
+ # [ cfg ( not ( unix ) ) ]
164
+ BytesOrWideString :: Bytes ( bytes ) => {
165
+ Path :: new ( crate :: str :: from_utf8 ( bytes ) . unwrap_or ( "<unknown>" ) )
185
166
}
186
- Ok ( ( ) )
187
- }
188
-
189
- /// Prints the filename and line number of the backtrace frame.
190
- ///
191
- /// See also `output`.
192
- fn output_fileline ( & mut self , symbol : & Symbol ) -> io:: Result < ( ) > {
193
167
#[ cfg( windows) ]
194
- let path_buf;
195
- let file = match symbol. filename_raw ( ) {
196
- #[ cfg( unix) ]
197
- Some ( BytesOrWideString :: Bytes ( bytes) ) => {
198
- use crate :: os:: unix:: prelude:: * ;
199
- Path :: new ( crate :: ffi:: OsStr :: from_bytes ( bytes) )
200
- }
201
- #[ cfg( not( unix) ) ]
202
- Some ( BytesOrWideString :: Bytes ( bytes) ) => {
203
- Path :: new ( crate :: str:: from_utf8 ( bytes) . unwrap_or ( "<unknown>" ) )
204
- }
205
- #[ cfg( windows) ]
206
- Some ( BytesOrWideString :: Wide ( wide) ) => {
207
- use crate :: os:: windows:: prelude:: * ;
208
- path_buf = crate :: ffi:: OsString :: from_wide ( wide) ;
209
- Path :: new ( & path_buf)
210
- }
211
- #[ cfg( not( windows) ) ]
212
- Some ( BytesOrWideString :: Wide ( _wide) ) => {
213
- Path :: new ( "<unknown>" )
214
- }
215
- None => return Ok ( ( ) ) ,
216
- } ;
217
- let line = match symbol. lineno ( ) {
218
- Some ( line) => line,
219
- None => return Ok ( ( ) ) ,
220
- } ;
221
- // prior line: " ##: {:2$} - func"
222
- self . out . write_all ( b"" ) ?;
223
- match self . format {
224
- PrintFormat :: Full => write ! ( self . out, " {:1$}" , "" , HEX_WIDTH ) ?,
225
- PrintFormat :: Short => write ! ( self . out, " " ) ?,
168
+ BytesOrWideString :: Wide ( wide) => {
169
+ use crate :: os:: windows:: prelude:: * ;
170
+ path_buf = crate :: ffi:: OsString :: from_wide ( wide) ;
171
+ Path :: new ( & path_buf)
226
172
}
227
-
228
- let mut already_printed = false ;
229
- if self . format == PrintFormat :: Short && file. is_absolute ( ) {
230
- if let Ok ( cwd) = env:: current_dir ( ) {
231
- if let Ok ( stripped) = file. strip_prefix ( & cwd) {
232
- if let Some ( s) = stripped. to_str ( ) {
233
- write ! ( self . out, " at .{}{}:{}" , path:: MAIN_SEPARATOR , s, line) ?;
234
- already_printed = true ;
235
- }
173
+ #[ cfg( not( windows) ) ]
174
+ BytesOrWideString :: Wide ( _wide) => {
175
+ Path :: new ( "<unknown>" )
176
+ }
177
+ } ;
178
+ if print_fmt == PrintFmt :: Short && file. is_absolute ( ) {
179
+ if let Ok ( cwd) = env:: current_dir ( ) {
180
+ if let Ok ( stripped) = file. strip_prefix ( & cwd) {
181
+ if let Some ( s) = stripped. to_str ( ) {
182
+ return write ! ( fmt, ".{}{}" , path:: MAIN_SEPARATOR , s) ;
236
183
}
237
184
}
238
185
}
239
- if !already_printed {
240
- write ! ( self . out, " at {}:{}" , file. display( ) , line) ?;
241
- }
242
-
243
- self . out . write_all ( b"\n " )
244
186
}
187
+ fmt:: Display :: fmt ( & file. display ( ) , fmt)
245
188
}
0 commit comments