@@ -12,19 +12,24 @@ use prelude::v1::*;
12
12
use io:: prelude:: * ;
13
13
14
14
use any:: Any ;
15
+ use cell:: Cell ;
15
16
use cell:: RefCell ;
17
+ use intrinsics;
16
18
use sys:: stdio:: Stderr ;
17
19
use sys_common:: backtrace;
18
20
use sys_common:: thread_info;
19
- use sys_common:: unwind;
21
+ use sys_common:: util;
22
+
23
+ thread_local ! { pub static PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
20
24
21
25
thread_local ! {
22
26
pub static LOCAL_STDERR : RefCell <Option <Box <Write + Send >>> = {
23
27
RefCell :: new( None )
24
28
}
25
29
}
26
30
27
- pub fn on_panic ( obj : & ( Any +Send ) , file : & ' static str , line : u32 ) {
31
+ fn log_panic ( obj : & ( Any +Send ) , file : & ' static str , line : u32 ,
32
+ log_backtrace : bool ) {
28
33
let msg = match obj. downcast_ref :: < & ' static str > ( ) {
29
34
Some ( s) => * s,
30
35
None => match obj. downcast_ref :: < String > ( ) {
@@ -35,37 +40,59 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
35
40
let mut err = Stderr :: new ( ) . ok ( ) ;
36
41
let thread = thread_info:: current_thread ( ) ;
37
42
let name = thread. as_ref ( ) . and_then ( |t| t. name ( ) ) . unwrap_or ( "<unnamed>" ) ;
43
+
44
+ let write = |err : & mut :: io:: Write | {
45
+ let _ = writeln ! ( err, "thread '{}' panicked at '{}', {}:{}" ,
46
+ name, msg, file, line) ;
47
+ if log_backtrace {
48
+ let _ = backtrace:: write ( err) ;
49
+ }
50
+ } ;
51
+
38
52
let prev = LOCAL_STDERR . with ( |s| s. borrow_mut ( ) . take ( ) ) ;
39
53
match ( prev, err. as_mut ( ) ) {
40
54
( Some ( mut stderr) , _) => {
41
- // FIXME: what to do when the thread printing panics?
42
- let _ = writeln ! ( stderr,
43
- "thread '{}' panicked at '{}', {}:{}\n " ,
44
- name, msg, file, line) ;
45
- if backtrace:: log_enabled ( ) {
46
- let _ = backtrace:: write ( & mut * stderr) ;
47
- }
55
+ write ( & mut * stderr) ;
48
56
let mut s = Some ( stderr) ;
49
57
LOCAL_STDERR . with ( |slot| {
50
58
* slot. borrow_mut ( ) = s. take ( ) ;
51
59
} ) ;
52
60
}
53
- ( None , Some ( ref mut err) ) => {
54
- let _ = writeln ! ( err, "thread '{}' panicked at '{}', {}:{}" ,
55
- name, msg, file, line) ;
56
- if backtrace:: log_enabled ( ) {
57
- let _ = backtrace:: write ( err) ;
58
- }
59
- }
61
+ ( None , Some ( ref mut err) ) => { write ( err) }
60
62
_ => { }
61
63
}
64
+ }
62
65
63
- // If this is a double panic, make sure that we printed a backtrace
64
- // for this panic.
65
- match err {
66
- Some ( ref mut err) if unwind:: panicking ( ) && !backtrace:: log_enabled ( ) => {
67
- let _ = backtrace:: write ( err) ;
68
- }
69
- _ => { }
66
+ pub fn on_panic ( obj : & ( Any +Send ) , file : & ' static str , line : u32 ) {
67
+ let panics = PANIC_COUNT . with ( |s| {
68
+ let count = s. get ( ) + 1 ;
69
+ s. set ( count) ;
70
+ count
71
+ } ) ;
72
+
73
+ // If this is the third nested call, on_panic triggered the last panic,
74
+ // otherwise the double-panic check would have aborted the process.
75
+ // Even if it is likely that on_panic was unable to log the backtrace,
76
+ // abort immediately to avoid infinite recursion, so that attaching a
77
+ // debugger provides a useable stacktrace.
78
+ if panics >= 3 {
79
+ util:: dumb_print ( format_args ! ( "thread panicked while processing \
80
+ panic. aborting.") ) ;
81
+ unsafe { intrinsics:: abort ( ) }
82
+ }
83
+
84
+ // If this is a double panic, make sure that we print a backtrace
85
+ // for this panic. Otherwise only print it if logging is enabled.
86
+ let log_backtrace = panics >= 2 || backtrace:: log_enabled ( ) ;
87
+ log_panic ( obj, file, line, log_backtrace) ;
88
+
89
+ if panics >= 2 {
90
+ // If a thread panics while it's already unwinding then we
91
+ // have limited options. Currently our preference is to
92
+ // just abort. In the future we may consider resuming
93
+ // unwinding or otherwise exiting the thread cleanly.
94
+ util:: dumb_print ( format_args ! ( "thread panicked while panicking. \
95
+ aborting.") ) ;
96
+ unsafe { intrinsics:: abort ( ) }
70
97
}
71
98
}
0 commit comments