]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Implementation of Rust stack unwinding | |
12 | //! | |
13 | //! For background on exception handling and stack unwinding please see | |
14 | //! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and | |
15 | //! documents linked from it. | |
16 | //! These are also good reads: | |
17 | //! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ | |
18 | //! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ | |
19 | //! http://www.airs.com/blog/index.php?s=exception+frames | |
20 | //! | |
21 | //! ## A brief summary | |
22 | //! | |
23 | //! Exception handling happens in two phases: a search phase and a cleanup phase. | |
24 | //! | |
25 | //! In both phases the unwinder walks stack frames from top to bottom using | |
26 | //! information from the stack frame unwind sections of the current process's | |
27 | //! modules ("module" here refers to an OS module, i.e. an executable or a | |
28 | //! dynamic library). | |
29 | //! | |
30 | //! For each stack frame, it invokes the associated "personality routine", whose | |
31 | //! address is also stored in the unwind info section. | |
32 | //! | |
33 | //! In the search phase, the job of a personality routine is to examine exception | |
34 | //! object being thrown, and to decide whether it should be caught at that stack | |
35 | //! frame. Once the handler frame has been identified, cleanup phase begins. | |
36 | //! | |
37 | //! In the cleanup phase, personality routines invoke cleanup code associated | |
38 | //! with their stack frames (i.e. destructors). Once stack has been unwound down | |
39 | //! to the handler frame level, unwinding stops and the last personality routine | |
40 | //! transfers control to its catch block. | |
41 | //! | |
42 | //! ## Frame unwind info registration | |
43 | //! | |
44 | //! Each module has its own frame unwind info section (usually ".eh_frame"), and | |
45 | //! unwinder needs to know about all of them in order for unwinding to be able to | |
46 | //! cross module boundaries. | |
47 | //! | |
48 | //! On some platforms, like Linux, this is achieved by dynamically enumerating | |
49 | //! currently loaded modules via the dl_iterate_phdr() API and finding all | |
50 | //! .eh_frame sections. | |
51 | //! | |
52 | //! Others, like Windows, require modules to actively register their unwind info | |
53 | //! sections by calling __register_frame_info() API at startup. In the latter | |
54 | //! case it is essential that there is only one copy of the unwinder runtime in | |
55 | //! the process. This is usually achieved by linking to the dynamic version of | |
56 | //! the unwind runtime. | |
57 | //! | |
58 | //! Currently Rust uses unwind runtime provided by libgcc. | |
59 | ||
60 | use prelude::v1::*; | |
61 | ||
62 | use any::Any; | |
c34b1796 | 63 | use boxed; |
1a4d82fc JJ |
64 | use cell::Cell; |
65 | use cmp; | |
85aaf69f | 66 | use panicking; |
1a4d82fc JJ |
67 | use fmt; |
68 | use intrinsics; | |
69 | use libc::c_void; | |
70 | use mem; | |
71 | use sync::atomic::{self, Ordering}; | |
c34b1796 | 72 | use sys_common::mutex::{Mutex, MUTEX_INIT}; |
1a4d82fc JJ |
73 | |
74 | use rt::libunwind as uw; | |
75 | ||
76 | struct Exception { | |
77 | uwe: uw::_Unwind_Exception, | |
85aaf69f | 78 | cause: Option<Box<Any + Send + 'static>>, |
1a4d82fc JJ |
79 | } |
80 | ||
9346a6ac | 81 | pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); |
1a4d82fc JJ |
82 | |
83 | // Variables used for invoking callbacks when a thread starts to unwind. | |
84 | // | |
85 | // For more information, see below. | |
c34b1796 | 86 | const MAX_CALLBACKS: usize = 16; |
85aaf69f SL |
87 | static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] = |
88 | [atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
89 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
90 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
91 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
92 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
93 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
94 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, | |
95 | atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT]; | |
96 | static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; | |
1a4d82fc JJ |
97 | |
98 | thread_local! { static PANICKING: Cell<bool> = Cell::new(false) } | |
99 | ||
100 | /// Invoke a closure, capturing the cause of panic if one occurs. | |
101 | /// | |
85aaf69f SL |
102 | /// This function will return `Ok(())` if the closure did not panic, and will |
103 | /// return `Err(cause)` if the closure panics. The `cause` returned is the | |
1a4d82fc JJ |
104 | /// object with which panic was originally invoked. |
105 | /// | |
106 | /// This function also is unsafe for a variety of reasons: | |
107 | /// | |
108 | /// * This is not safe to call in a nested fashion. The unwinding | |
109 | /// interface for Rust is designed to have at most one try/catch block per | |
110 | /// thread, not multiple. No runtime checking is currently performed to uphold | |
111 | /// this invariant, so this function is not safe. A nested try/catch block | |
112 | /// may result in corruption of the outer try/catch block's state, especially | |
113 | /// if this is used within a thread itself. | |
114 | /// | |
115 | /// * It is not sound to trigger unwinding while already unwinding. Rust threads | |
116 | /// have runtime checks in place to ensure this invariant, but it is not | |
117 | /// guaranteed that a rust thread is in place when invoking this function. | |
118 | /// Unwinding twice can lead to resource leaks where some destructors are not | |
119 | /// run. | |
120 | pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { | |
121 | let mut f = Some(f); | |
122 | ||
123 | let prev = PANICKING.with(|s| s.get()); | |
124 | PANICKING.with(|s| s.set(false)); | |
125 | let ep = rust_try(try_fn::<F>, &mut f as *mut _ as *mut c_void); | |
126 | PANICKING.with(|s| s.set(prev)); | |
127 | return if ep.is_null() { | |
128 | Ok(()) | |
129 | } else { | |
130 | let my_ep = ep as *mut Exception; | |
131 | rtdebug!("caught {}", (*my_ep).uwe.exception_class); | |
132 | let cause = (*my_ep).cause.take(); | |
133 | uw::_Unwind_DeleteException(ep); | |
134 | Err(cause.unwrap()) | |
135 | }; | |
136 | ||
137 | extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) { | |
138 | let opt_closure = opt_closure as *mut Option<F>; | |
139 | unsafe { (*opt_closure).take().unwrap()(); } | |
140 | } | |
141 | ||
142 | #[link(name = "rustrt_native", kind = "static")] | |
143 | #[cfg(not(test))] | |
144 | extern {} | |
145 | ||
146 | extern { | |
147 | // Rust's try-catch | |
148 | // When f(...) returns normally, the return value is null. | |
149 | // When f(...) throws, the return value is a pointer to the caught | |
150 | // exception object. | |
151 | fn rust_try(f: extern fn(*mut c_void), | |
152 | data: *mut c_void) -> *mut uw::_Unwind_Exception; | |
153 | } | |
154 | } | |
155 | ||
85aaf69f | 156 | /// Determines whether the current thread is unwinding because of panic. |
1a4d82fc JJ |
157 | pub fn panicking() -> bool { |
158 | PANICKING.with(|s| s.get()) | |
159 | } | |
160 | ||
161 | // An uninlined, unmangled function upon which to slap yer breakpoints | |
162 | #[inline(never)] | |
163 | #[no_mangle] | |
85aaf69f SL |
164 | #[allow(private_no_mangle_fns)] |
165 | fn rust_panic(cause: Box<Any + Send + 'static>) -> ! { | |
1a4d82fc JJ |
166 | rtdebug!("begin_unwind()"); |
167 | ||
168 | unsafe { | |
c34b1796 | 169 | let exception: Box<_> = box Exception { |
1a4d82fc JJ |
170 | uwe: uw::_Unwind_Exception { |
171 | exception_class: rust_exception_class(), | |
172 | exception_cleanup: exception_cleanup, | |
173 | private: [0; uw::unwinder_private_data_size], | |
174 | }, | |
175 | cause: Some(cause), | |
176 | }; | |
c34b1796 AL |
177 | let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception; |
178 | let error = uw::_Unwind_RaiseException(exception_param); | |
179 | rtabort!("Could not unwind stack, error = {}", error as isize) | |
1a4d82fc JJ |
180 | } |
181 | ||
182 | extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, | |
183 | exception: *mut uw::_Unwind_Exception) { | |
184 | rtdebug!("exception_cleanup()"); | |
185 | unsafe { | |
c34b1796 | 186 | let _: Box<Exception> = Box::from_raw(exception as *mut Exception); |
1a4d82fc JJ |
187 | } |
188 | } | |
189 | } | |
190 | ||
191 | // Rust's exception class identifier. This is used by personality routines to | |
192 | // determine whether the exception was thrown by their own runtime. | |
193 | fn rust_exception_class() -> uw::_Unwind_Exception_Class { | |
194 | // M O Z \0 R U S T -- vendor, language | |
195 | 0x4d4f5a_00_52555354 | |
196 | } | |
197 | ||
198 | // We could implement our personality routine in pure Rust, however exception | |
199 | // info decoding is tedious. More importantly, personality routines have to | |
200 | // handle various platform quirks, which are not fun to maintain. For this | |
201 | // reason, we attempt to reuse personality routine of the C language: | |
202 | // __gcc_personality_v0. | |
203 | // | |
204 | // Since C does not support exception catching, __gcc_personality_v0 simply | |
205 | // always returns _URC_CONTINUE_UNWIND in search phase, and always returns | |
206 | // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. | |
207 | // | |
208 | // This is pretty close to Rust's exception handling approach, except that Rust | |
209 | // does have a single "catch-all" handler at the bottom of each thread's stack. | |
210 | // So we have two versions of the personality routine: | |
211 | // - rust_eh_personality, used by all cleanup landing pads, which never catches, | |
212 | // so the behavior of __gcc_personality_v0 is perfectly adequate there, and | |
213 | // - rust_eh_personality_catch, used only by rust_try(), which always catches. | |
214 | // | |
215 | // Note, however, that for implementation simplicity, rust_eh_personality_catch | |
216 | // lacks code to install a landing pad, so in order to obtain exception object | |
217 | // pointer (which it needs to return upstream), rust_try() employs another trick: | |
218 | // it calls into the nested rust_try_inner(), whose landing pad does not resume | |
219 | // unwinds. Instead, it extracts the exception pointer and performs a "normal" | |
220 | // return. | |
221 | // | |
222 | // See also: rt/rust_try.ll | |
223 | ||
224 | #[cfg(all(not(target_arch = "arm"), | |
225 | not(all(windows, target_arch = "x86_64")), | |
226 | not(test)))] | |
227 | #[doc(hidden)] | |
228 | pub mod eabi { | |
229 | use rt::libunwind as uw; | |
230 | use libc::c_int; | |
231 | ||
232 | extern "C" { | |
233 | fn __gcc_personality_v0(version: c_int, | |
234 | actions: uw::_Unwind_Action, | |
235 | exception_class: uw::_Unwind_Exception_Class, | |
236 | ue_header: *mut uw::_Unwind_Exception, | |
237 | context: *mut uw::_Unwind_Context) | |
238 | -> uw::_Unwind_Reason_Code; | |
239 | } | |
240 | ||
241 | #[lang="eh_personality"] | |
242 | #[no_mangle] // referenced from rust_try.ll | |
85aaf69f | 243 | #[allow(private_no_mangle_fns)] |
1a4d82fc JJ |
244 | extern fn rust_eh_personality( |
245 | version: c_int, | |
246 | actions: uw::_Unwind_Action, | |
247 | exception_class: uw::_Unwind_Exception_Class, | |
248 | ue_header: *mut uw::_Unwind_Exception, | |
249 | context: *mut uw::_Unwind_Context | |
250 | ) -> uw::_Unwind_Reason_Code | |
251 | { | |
252 | unsafe { | |
253 | __gcc_personality_v0(version, actions, exception_class, ue_header, | |
254 | context) | |
255 | } | |
256 | } | |
257 | ||
258 | #[no_mangle] // referenced from rust_try.ll | |
259 | pub extern "C" fn rust_eh_personality_catch( | |
260 | _version: c_int, | |
261 | actions: uw::_Unwind_Action, | |
262 | _exception_class: uw::_Unwind_Exception_Class, | |
263 | _ue_header: *mut uw::_Unwind_Exception, | |
264 | _context: *mut uw::_Unwind_Context | |
265 | ) -> uw::_Unwind_Reason_Code | |
266 | { | |
267 | ||
268 | if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase | |
269 | uw::_URC_HANDLER_FOUND // catch! | |
270 | } | |
271 | else { // cleanup phase | |
272 | uw::_URC_INSTALL_CONTEXT | |
273 | } | |
274 | } | |
275 | } | |
276 | ||
277 | // iOS on armv7 is using SjLj exceptions and therefore requires to use | |
278 | // a specialized personality routine: __gcc_personality_sj0 | |
279 | ||
280 | #[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] | |
281 | #[doc(hidden)] | |
282 | pub mod eabi { | |
283 | use rt::libunwind as uw; | |
284 | use libc::c_int; | |
285 | ||
286 | extern "C" { | |
287 | fn __gcc_personality_sj0(version: c_int, | |
288 | actions: uw::_Unwind_Action, | |
289 | exception_class: uw::_Unwind_Exception_Class, | |
290 | ue_header: *mut uw::_Unwind_Exception, | |
291 | context: *mut uw::_Unwind_Context) | |
292 | -> uw::_Unwind_Reason_Code; | |
293 | } | |
294 | ||
295 | #[lang="eh_personality"] | |
296 | #[no_mangle] // referenced from rust_try.ll | |
297 | pub extern "C" fn rust_eh_personality( | |
298 | version: c_int, | |
299 | actions: uw::_Unwind_Action, | |
300 | exception_class: uw::_Unwind_Exception_Class, | |
301 | ue_header: *mut uw::_Unwind_Exception, | |
302 | context: *mut uw::_Unwind_Context | |
303 | ) -> uw::_Unwind_Reason_Code | |
304 | { | |
305 | unsafe { | |
306 | __gcc_personality_sj0(version, actions, exception_class, ue_header, | |
307 | context) | |
308 | } | |
309 | } | |
310 | ||
311 | #[no_mangle] // referenced from rust_try.ll | |
312 | pub extern "C" fn rust_eh_personality_catch( | |
313 | _version: c_int, | |
314 | actions: uw::_Unwind_Action, | |
315 | _exception_class: uw::_Unwind_Exception_Class, | |
316 | _ue_header: *mut uw::_Unwind_Exception, | |
317 | _context: *mut uw::_Unwind_Context | |
318 | ) -> uw::_Unwind_Reason_Code | |
319 | { | |
320 | if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase | |
321 | uw::_URC_HANDLER_FOUND // catch! | |
322 | } | |
323 | else { // cleanup phase | |
324 | unsafe { | |
325 | __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, | |
326 | _context) | |
327 | } | |
328 | } | |
329 | } | |
330 | } | |
331 | ||
332 | ||
333 | // ARM EHABI uses a slightly different personality routine signature, | |
334 | // but otherwise works the same. | |
335 | #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] | |
336 | #[doc(hidden)] | |
337 | pub mod eabi { | |
338 | use rt::libunwind as uw; | |
339 | use libc::c_int; | |
340 | ||
341 | extern "C" { | |
342 | fn __gcc_personality_v0(state: uw::_Unwind_State, | |
343 | ue_header: *mut uw::_Unwind_Exception, | |
344 | context: *mut uw::_Unwind_Context) | |
345 | -> uw::_Unwind_Reason_Code; | |
346 | } | |
347 | ||
348 | #[lang="eh_personality"] | |
349 | #[no_mangle] // referenced from rust_try.ll | |
85aaf69f | 350 | #[allow(private_no_mangle_fns)] |
1a4d82fc JJ |
351 | extern "C" fn rust_eh_personality( |
352 | state: uw::_Unwind_State, | |
353 | ue_header: *mut uw::_Unwind_Exception, | |
354 | context: *mut uw::_Unwind_Context | |
355 | ) -> uw::_Unwind_Reason_Code | |
356 | { | |
357 | unsafe { | |
358 | __gcc_personality_v0(state, ue_header, context) | |
359 | } | |
360 | } | |
361 | ||
362 | #[no_mangle] // referenced from rust_try.ll | |
363 | pub extern "C" fn rust_eh_personality_catch( | |
364 | state: uw::_Unwind_State, | |
365 | _ue_header: *mut uw::_Unwind_Exception, | |
366 | _context: *mut uw::_Unwind_Context | |
367 | ) -> uw::_Unwind_Reason_Code | |
368 | { | |
369 | if (state as c_int & uw::_US_ACTION_MASK as c_int) | |
370 | == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase | |
371 | uw::_URC_HANDLER_FOUND // catch! | |
372 | } | |
373 | else { // cleanup phase | |
374 | uw::_URC_INSTALL_CONTEXT | |
375 | } | |
376 | } | |
377 | } | |
378 | ||
379 | // Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) | |
380 | // | |
381 | // This looks a bit convoluted because rather than implementing a native SEH handler, | |
382 | // GCC reuses the same personality routine as for the other architectures by wrapping it | |
383 | // with an "API translator" layer (_GCC_specific_handler). | |
384 | ||
385 | #[cfg(all(windows, target_arch = "x86_64", not(test)))] | |
386 | #[doc(hidden)] | |
387 | #[allow(non_camel_case_types, non_snake_case)] | |
388 | pub mod eabi { | |
389 | pub use self::EXCEPTION_DISPOSITION::*; | |
390 | use rt::libunwind as uw; | |
391 | use libc::{c_void, c_int}; | |
392 | ||
393 | #[repr(C)] | |
1a4d82fc JJ |
394 | pub struct EXCEPTION_RECORD; |
395 | #[repr(C)] | |
1a4d82fc JJ |
396 | pub struct CONTEXT; |
397 | #[repr(C)] | |
1a4d82fc JJ |
398 | pub struct DISPATCHER_CONTEXT; |
399 | ||
400 | #[repr(C)] | |
c34b1796 | 401 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
402 | pub enum EXCEPTION_DISPOSITION { |
403 | ExceptionContinueExecution, | |
404 | ExceptionContinueSearch, | |
405 | ExceptionNestedException, | |
406 | ExceptionCollidedUnwind | |
407 | } | |
408 | ||
409 | type _Unwind_Personality_Fn = | |
410 | extern "C" fn( | |
411 | version: c_int, | |
412 | actions: uw::_Unwind_Action, | |
413 | exception_class: uw::_Unwind_Exception_Class, | |
414 | ue_header: *mut uw::_Unwind_Exception, | |
415 | context: *mut uw::_Unwind_Context | |
416 | ) -> uw::_Unwind_Reason_Code; | |
417 | ||
418 | extern "C" { | |
419 | fn __gcc_personality_seh0( | |
420 | exceptionRecord: *mut EXCEPTION_RECORD, | |
421 | establisherFrame: *mut c_void, | |
422 | contextRecord: *mut CONTEXT, | |
423 | dispatcherContext: *mut DISPATCHER_CONTEXT | |
424 | ) -> EXCEPTION_DISPOSITION; | |
425 | ||
426 | fn _GCC_specific_handler( | |
427 | exceptionRecord: *mut EXCEPTION_RECORD, | |
428 | establisherFrame: *mut c_void, | |
429 | contextRecord: *mut CONTEXT, | |
430 | dispatcherContext: *mut DISPATCHER_CONTEXT, | |
431 | personality: _Unwind_Personality_Fn | |
432 | ) -> EXCEPTION_DISPOSITION; | |
433 | } | |
434 | ||
435 | #[lang="eh_personality"] | |
436 | #[no_mangle] // referenced from rust_try.ll | |
85aaf69f | 437 | #[allow(private_no_mangle_fns)] |
1a4d82fc JJ |
438 | extern "C" fn rust_eh_personality( |
439 | exceptionRecord: *mut EXCEPTION_RECORD, | |
440 | establisherFrame: *mut c_void, | |
441 | contextRecord: *mut CONTEXT, | |
442 | dispatcherContext: *mut DISPATCHER_CONTEXT | |
443 | ) -> EXCEPTION_DISPOSITION | |
444 | { | |
445 | unsafe { | |
446 | __gcc_personality_seh0(exceptionRecord, establisherFrame, | |
447 | contextRecord, dispatcherContext) | |
448 | } | |
449 | } | |
450 | ||
451 | #[no_mangle] // referenced from rust_try.ll | |
452 | pub extern "C" fn rust_eh_personality_catch( | |
453 | exceptionRecord: *mut EXCEPTION_RECORD, | |
454 | establisherFrame: *mut c_void, | |
455 | contextRecord: *mut CONTEXT, | |
456 | dispatcherContext: *mut DISPATCHER_CONTEXT | |
457 | ) -> EXCEPTION_DISPOSITION | |
458 | { | |
459 | extern "C" fn inner( | |
460 | _version: c_int, | |
461 | actions: uw::_Unwind_Action, | |
462 | _exception_class: uw::_Unwind_Exception_Class, | |
463 | _ue_header: *mut uw::_Unwind_Exception, | |
464 | _context: *mut uw::_Unwind_Context | |
465 | ) -> uw::_Unwind_Reason_Code | |
466 | { | |
467 | if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase | |
468 | uw::_URC_HANDLER_FOUND // catch! | |
469 | } | |
470 | else { // cleanup phase | |
471 | uw::_URC_INSTALL_CONTEXT | |
472 | } | |
473 | } | |
474 | ||
475 | unsafe { | |
476 | _GCC_specific_handler(exceptionRecord, establisherFrame, | |
477 | contextRecord, dispatcherContext, | |
478 | inner) | |
479 | } | |
480 | } | |
481 | } | |
482 | ||
483 | #[cfg(not(test))] | |
484 | /// Entry point of panic from the libcore crate. | |
485 | #[lang = "panic_fmt"] | |
486 | pub extern fn rust_begin_unwind(msg: fmt::Arguments, | |
9346a6ac | 487 | file: &'static str, line: u32) -> ! { |
1a4d82fc JJ |
488 | begin_unwind_fmt(msg, &(file, line)) |
489 | } | |
490 | ||
491 | /// The entry point for unwinding with a formatted message. | |
492 | /// | |
493 | /// This is designed to reduce the amount of code required at the call | |
494 | /// site as much as possible (so that `panic!()` has as low an impact | |
495 | /// on (e.g.) the inlining of other functions as possible), by moving | |
496 | /// the actual formatting into this shared place. | |
497 | #[inline(never)] #[cold] | |
9346a6ac | 498 | pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { |
85aaf69f | 499 | use fmt::Write; |
1a4d82fc JJ |
500 | |
501 | // We do two allocations here, unfortunately. But (a) they're | |
502 | // required with the current scheme, and (b) we don't handle | |
503 | // panic + OOM properly anyway (see comment in begin_unwind | |
504 | // below). | |
505 | ||
506 | let mut s = String::new(); | |
507 | let _ = write!(&mut s, "{}", msg); | |
c34b1796 | 508 | begin_unwind_inner(Box::new(s), file_line) |
1a4d82fc JJ |
509 | } |
510 | ||
511 | /// This is the entry point of unwinding for panic!() and assert!(). | |
512 | #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible | |
9346a6ac | 513 | #[cfg(stage0)] |
c34b1796 | 514 | pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, usize)) -> ! { |
1a4d82fc JJ |
515 | // Note that this should be the only allocation performed in this code path. |
516 | // Currently this means that panic!() on OOM will invoke this code path, | |
517 | // but then again we're not really ready for panic on OOM anyway. If | |
518 | // we do start doing this, then we should propagate this allocation to | |
519 | // be performed in the parent of this thread instead of the thread that's | |
520 | // panicking. | |
521 | ||
9346a6ac AL |
522 | // see below for why we do the `Any` coercion here. |
523 | let (file, line) = *file_line; | |
524 | begin_unwind_inner(Box::new(msg), &(file, line as u32)) | |
525 | } | |
526 | ||
527 | /// This is the entry point of unwinding for panic!() and assert!(). | |
528 | #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible | |
529 | #[cfg(not(stage0))] | |
530 | pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! { | |
531 | // Note that this should be the only allocation performed in this code path. | |
532 | // Currently this means that panic!() on OOM will invoke this code path, | |
533 | // but then again we're not really ready for panic on OOM anyway. If | |
534 | // we do start doing this, then we should propagate this allocation to | |
535 | // be performed in the parent of this thread instead of the thread that's | |
536 | // panicking. | |
537 | ||
1a4d82fc | 538 | // see below for why we do the `Any` coercion here. |
c34b1796 | 539 | begin_unwind_inner(Box::new(msg), file_line) |
1a4d82fc JJ |
540 | } |
541 | ||
542 | /// The core of the unwinding. | |
543 | /// | |
544 | /// This is non-generic to avoid instantiation bloat in other crates | |
545 | /// (which makes compilation of small crates noticeably slower). (Note: | |
546 | /// we need the `Any` object anyway, we're not just creating it to | |
547 | /// avoid being generic.) | |
548 | /// | |
549 | /// Doing this split took the LLVM IR line counts of `fn main() { panic!() | |
550 | /// }` from ~1900/3700 (-O/no opts) to 180/590. | |
551 | #[inline(never)] #[cold] // this is the slow path, please never inline this | |
c34b1796 | 552 | fn begin_unwind_inner(msg: Box<Any + Send>, |
9346a6ac | 553 | file_line: &(&'static str, u32)) -> ! { |
c34b1796 AL |
554 | // Make sure the default failure handler is registered before we look at the |
555 | // callbacks. We also use a raw sys-based mutex here instead of a | |
556 | // `std::sync` one as accessing TLS can cause weird recursive problems (and | |
557 | // we don't need poison checking). | |
558 | unsafe { | |
559 | static LOCK: Mutex = MUTEX_INIT; | |
560 | static mut INIT: bool = false; | |
561 | LOCK.lock(); | |
562 | if !INIT { | |
563 | register(panicking::on_panic); | |
564 | INIT = true; | |
565 | } | |
566 | LOCK.unlock(); | |
567 | } | |
1a4d82fc JJ |
568 | |
569 | // First, invoke call the user-defined callbacks triggered on thread panic. | |
570 | // | |
571 | // By the time that we see a callback has been registered (by reading | |
572 | // MAX_CALLBACKS), the actual callback itself may have not been stored yet, | |
573 | // so we just chalk it up to a race condition and move on to the next | |
574 | // callback. Additionally, CALLBACK_CNT may briefly be higher than | |
575 | // MAX_CALLBACKS, so we're sure to clamp it as necessary. | |
576 | let callbacks = { | |
577 | let amt = CALLBACK_CNT.load(Ordering::SeqCst); | |
85aaf69f | 578 | &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)] |
1a4d82fc | 579 | }; |
85aaf69f | 580 | for cb in callbacks { |
1a4d82fc JJ |
581 | match cb.load(Ordering::SeqCst) { |
582 | 0 => {} | |
583 | n => { | |
584 | let f: Callback = unsafe { mem::transmute(n) }; | |
585 | let (file, line) = *file_line; | |
586 | f(&*msg, file, line); | |
587 | } | |
588 | } | |
589 | }; | |
590 | ||
591 | // Now that we've run all the necessary unwind callbacks, we actually | |
592 | // perform the unwinding. | |
593 | if panicking() { | |
594 | // If a thread panics while it's already unwinding then we | |
595 | // have limited options. Currently our preference is to | |
596 | // just abort. In the future we may consider resuming | |
597 | // unwinding or otherwise exiting the thread cleanly. | |
598 | rterrln!("thread panicked while panicking. aborting."); | |
599 | unsafe { intrinsics::abort() } | |
600 | } | |
601 | PANICKING.with(|s| s.set(true)); | |
602 | rust_panic(msg); | |
603 | } | |
604 | ||
605 | /// Register a callback to be invoked when a thread unwinds. | |
606 | /// | |
607 | /// This is an unsafe and experimental API which allows for an arbitrary | |
608 | /// callback to be invoked when a thread panics. This callback is invoked on both | |
609 | /// the initial unwinding and a double unwinding if one occurs. Additionally, | |
610 | /// the local `Task` will be in place for the duration of the callback, and | |
611 | /// the callback must ensure that it remains in place once the callback returns. | |
612 | /// | |
613 | /// Only a limited number of callbacks can be registered, and this function | |
614 | /// returns whether the callback was successfully registered or not. It is not | |
615 | /// currently possible to unregister a callback once it has been registered. | |
1a4d82fc JJ |
616 | pub unsafe fn register(f: Callback) -> bool { |
617 | match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) { | |
618 | // The invocation code has knowledge of this window where the count has | |
619 | // been incremented, but the callback has not been stored. We're | |
620 | // guaranteed that the slot we're storing into is 0. | |
621 | n if n < MAX_CALLBACKS => { | |
622 | let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst); | |
623 | rtassert!(prev == 0); | |
624 | true | |
625 | } | |
626 | // If we accidentally bumped the count too high, pull it back. | |
627 | _ => { | |
628 | CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst); | |
629 | false | |
630 | } | |
631 | } | |
632 | } |