]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/unwind.rs
Add basic backtrace functionality
[rust.git] / src / libstd / rt / unwind.rs
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 // Exception handling happens in two phases: a search phase and a cleanup phase.
23 //
24 // In both phases the unwinder walks stack frames from top to bottom using
25 // information from the stack frame unwind sections of the current process's
26 // modules ("module" here refers to an OS module, i.e. an executable or a
27 // dynamic library).
28 //
29 // For each stack frame, it invokes the associated "personality routine", whose
30 // address is also stored in the unwind info section.
31 //
32 // In the search phase, the job of a personality routine is to examine exception
33 // object being thrown, and to decide whether it should be caught at that stack
34 // frame.  Once the handler frame has been identified, cleanup phase begins.
35 //
36 // In the cleanup phase, personality routines invoke cleanup code associated
37 // with their stack frames (i.e. destructors).  Once stack has been unwound down
38 // to the handler frame level, unwinding stops and the last personality routine
39 // transfers control to its' catch block.
40 //
41 // ~~~ Frame unwind info registration ~~~
42 // Each module has its' own frame unwind info section (usually ".eh_frame"), and
43 // unwinder needs to know about all of them in order for unwinding to be able to
44 // cross module boundaries.
45 //
46 // On some platforms, like Linux, this is achieved by dynamically enumerating
47 // currently loaded modules via the dl_iterate_phdr() API and finding all
48 // .eh_frame sections.
49 //
50 // Others, like Windows, require modules to actively register their unwind info
51 // sections by calling __register_frame_info() API at startup.  In the latter
52 // case it is essential that there is only one copy of the unwinder runtime in
53 // the process.  This is usually achieved by linking to the dynamic version of
54 // the unwind runtime.
55 //
56 // Currently Rust uses unwind runtime provided by libgcc.
57
58 use any::{Any, AnyRefExt};
59 use c_str::CString;
60 use cast;
61 use fmt;
62 use kinds::Send;
63 use mem;
64 use option::{Some, None, Option};
65 use prelude::drop;
66 use ptr::RawPtr;
67 use result::{Err, Ok};
68 use rt::backtrace;
69 use rt::local::Local;
70 use rt::task::Task;
71 use str::Str;
72 use task::TaskResult;
73 use intrinsics;
74
75 use uw = rt::libunwind;
76
77 pub struct Unwinder {
78     priv unwinding: bool,
79     priv cause: Option<~Any>
80 }
81
82 impl Unwinder {
83     pub fn new() -> Unwinder {
84         Unwinder {
85             unwinding: false,
86             cause: None,
87         }
88     }
89
90     pub fn unwinding(&self) -> bool {
91         self.unwinding
92     }
93
94     pub fn try(&mut self, f: ||) {
95         use raw::Closure;
96         use libc::{c_void};
97
98         unsafe {
99             let closure: Closure = cast::transmute(f);
100             let ep = rust_try(try_fn, closure.code as *c_void,
101                               closure.env as *c_void);
102             if !ep.is_null() {
103                 rtdebug!("caught {}", (*ep).exception_class);
104                 uw::_Unwind_DeleteException(ep);
105             }
106         }
107
108         extern fn try_fn(code: *c_void, env: *c_void) {
109             unsafe {
110                 let closure: || = cast::transmute(Closure {
111                     code: code as *(),
112                     env: env as *(),
113                 });
114                 closure();
115             }
116         }
117
118         extern {
119             // Rust's try-catch
120             // When f(...) returns normally, the return value is null.
121             // When f(...) throws, the return value is a pointer to the caught
122             // exception object.
123             fn rust_try(f: extern "C" fn(*c_void, *c_void),
124                         code: *c_void,
125                         data: *c_void) -> *uw::_Unwind_Exception;
126         }
127     }
128
129     pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
130         rtdebug!("begin_unwind()");
131
132         self.unwinding = true;
133         self.cause = Some(cause);
134
135         rust_fail();
136
137         // An uninlined, unmangled function upon which to slap yer breakpoints
138         #[inline(never)]
139         #[no_mangle]
140         fn rust_fail() -> ! {
141             unsafe {
142                 let exception = ~uw::_Unwind_Exception {
143                     exception_class: rust_exception_class(),
144                     exception_cleanup: exception_cleanup,
145                     private: [0, ..uw::unwinder_private_data_size],
146                 };
147                 let error = uw::_Unwind_RaiseException(cast::transmute(exception));
148                 rtabort!("Could not unwind stack, error = {}", error as int)
149             }
150
151             extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
152                                             exception: *uw::_Unwind_Exception) {
153                 rtdebug!("exception_cleanup()");
154                 unsafe {
155                     let _: ~uw::_Unwind_Exception = cast::transmute(exception);
156                 }
157             }
158         }
159     }
160
161     pub fn result(&mut self) -> TaskResult {
162         if self.unwinding {
163             Err(self.cause.take().unwrap())
164         } else {
165             Ok(())
166         }
167     }
168 }
169
170 // Rust's exception class identifier.  This is used by personality routines to
171 // determine whether the exception was thrown by their own runtime.
172 fn rust_exception_class() -> uw::_Unwind_Exception_Class {
173     // M O Z \0  R U S T -- vendor, language
174     0x4d4f5a_00_52555354
175 }
176
177 // We could implement our personality routine in pure Rust, however exception
178 // info decoding is tedious.  More importantly, personality routines have to
179 // handle various platform quirks, which are not fun to maintain.  For this
180 // reason, we attempt to reuse personality routine of the C language:
181 // __gcc_personality_v0.
182 //
183 // Since C does not support exception catching, __gcc_personality_v0 simply
184 // always returns _URC_CONTINUE_UNWIND in search phase, and always returns
185 // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
186 //
187 // This is pretty close to Rust's exception handling approach, except that Rust
188 // does have a single "catch-all" handler at the bottom of each task's stack.
189 // So we have two versions:
190 // - rust_eh_personality, used by all cleanup landing pads, which never catches,
191 //   so the behavior of __gcc_personality_v0 is perfectly adequate there, and
192 // - rust_eh_personality_catch, used only by rust_try(), which always catches.
193 //   This is achieved by overriding the return value in search phase to always
194 //   say "catch!".
195
196 #[cfg(not(target_arch = "arm"), not(test))]
197 #[doc(hidden)]
198 #[allow(visible_private_types)]
199 pub mod eabi {
200     use uw = rt::libunwind;
201     use libc::c_int;
202
203     extern "C" {
204         fn __gcc_personality_v0(version: c_int,
205                                 actions: uw::_Unwind_Action,
206                                 exception_class: uw::_Unwind_Exception_Class,
207                                 ue_header: *uw::_Unwind_Exception,
208                                 context: *uw::_Unwind_Context)
209             -> uw::_Unwind_Reason_Code;
210     }
211
212     #[lang="eh_personality"]
213     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
214     pub extern "C" fn rust_eh_personality(
215         version: c_int,
216         actions: uw::_Unwind_Action,
217         exception_class: uw::_Unwind_Exception_Class,
218         ue_header: *uw::_Unwind_Exception,
219         context: *uw::_Unwind_Context
220     ) -> uw::_Unwind_Reason_Code
221     {
222         unsafe {
223             __gcc_personality_v0(version, actions, exception_class, ue_header,
224                                  context)
225         }
226     }
227
228     #[no_mangle] // referenced from rust_try.ll
229     pub extern "C" fn rust_eh_personality_catch(
230         version: c_int,
231         actions: uw::_Unwind_Action,
232         exception_class: uw::_Unwind_Exception_Class,
233         ue_header: *uw::_Unwind_Exception,
234         context: *uw::_Unwind_Context
235     ) -> uw::_Unwind_Reason_Code
236     {
237         if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
238             uw::_URC_HANDLER_FOUND // catch!
239         }
240         else { // cleanup phase
241             unsafe {
242                  __gcc_personality_v0(version, actions, exception_class, ue_header,
243                                       context)
244             }
245         }
246     }
247 }
248
249 // ARM EHABI uses a slightly different personality routine signature,
250 // but otherwise works the same.
251 #[cfg(target_arch = "arm", not(test))]
252 #[allow(visible_private_types)]
253 pub mod eabi {
254     use uw = rt::libunwind;
255     use libc::c_int;
256
257     extern "C" {
258         fn __gcc_personality_v0(state: uw::_Unwind_State,
259                                 ue_header: *uw::_Unwind_Exception,
260                                 context: *uw::_Unwind_Context)
261             -> uw::_Unwind_Reason_Code;
262     }
263
264     #[lang="eh_personality"]
265     #[no_mangle] // so we can reference it by name from middle/trans/base.rs
266     pub extern "C" fn rust_eh_personality(
267         state: uw::_Unwind_State,
268         ue_header: *uw::_Unwind_Exception,
269         context: *uw::_Unwind_Context
270     ) -> uw::_Unwind_Reason_Code
271     {
272         unsafe {
273             __gcc_personality_v0(state, ue_header, context)
274         }
275     }
276
277     #[no_mangle] // referenced from rust_try.ll
278     pub extern "C" fn rust_eh_personality_catch(
279         state: uw::_Unwind_State,
280         ue_header: *uw::_Unwind_Exception,
281         context: *uw::_Unwind_Context
282     ) -> uw::_Unwind_Reason_Code
283     {
284         if (state as c_int & uw::_US_ACTION_MASK as c_int)
285                            == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
286             uw::_URC_HANDLER_FOUND // catch!
287         }
288         else { // cleanup phase
289             unsafe {
290                  __gcc_personality_v0(state, ue_header, context)
291             }
292         }
293     }
294 }
295
296 #[cold]
297 #[lang="fail_"]
298 #[cfg(not(test))]
299 pub fn fail_(expr: *u8, file: *u8, line: uint) -> ! {
300     begin_unwind_raw(expr, file, line);
301 }
302
303 #[cold]
304 #[lang="fail_bounds_check"]
305 #[cfg(not(test))]
306 pub fn fail_bounds_check(file: *u8, line: uint, index: uint, len: uint) -> ! {
307     use c_str::ToCStr;
308
309     let msg = format!("index out of bounds: the len is {} but the index is {}",
310                       len as uint, index as uint);
311     msg.with_c_str(|buf| fail_(buf as *u8, file, line))
312 }
313
314 /// This is the entry point of unwinding for things like lang items and such.
315 /// The arguments are normally generated by the compiler, and need to
316 /// have static lifetimes.
317 #[inline(never)] #[cold] // this is the slow path, please never inline this
318 pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! {
319     use libc::c_char;
320     #[inline]
321     fn static_char_ptr(p: *u8) -> &'static str {
322         let s = unsafe { CString::new(p as *c_char, false) };
323         match s.as_str() {
324             Some(s) => unsafe { cast::transmute::<&str, &'static str>(s) },
325             None => rtabort!("message wasn't utf8?")
326         }
327     }
328
329     let msg = static_char_ptr(msg);
330     let file = static_char_ptr(file);
331
332     begin_unwind(msg, file, line as uint)
333 }
334
335 /// The entry point for unwinding with a formatted message.
336 ///
337 /// This is designed to reduce the amount of code required at the call
338 /// site as much as possible (so that `fail!()` has as low an impact
339 /// on (e.g.) the inlining of other functions as possible), by moving
340 /// the actual formatting into this shared place.
341 #[inline(never)] #[cold]
342 pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
343     // We do two allocations here, unfortunately. But (a) they're
344     // required with the current scheme, and (b) we don't handle
345     // failure + OOM properly anyway (see comment in begin_unwind
346     // below).
347     begin_unwind_inner(~fmt::format(msg), file, line)
348 }
349
350 /// This is the entry point of unwinding for fail!() and assert!().
351 #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
352 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
353     // Note that this should be the only allocation performed in this code path.
354     // Currently this means that fail!() on OOM will invoke this code path,
355     // but then again we're not really ready for failing on OOM anyway. If
356     // we do start doing this, then we should propagate this allocation to
357     // be performed in the parent of this task instead of the task that's
358     // failing.
359
360     // see below for why we do the `Any` coercion here.
361     begin_unwind_inner(~msg, file, line)
362 }
363
364
365 /// The core of the unwinding.
366 ///
367 /// This is non-generic to avoid instantiation bloat in other crates
368 /// (which makes compilation of small crates noticably slower). (Note:
369 /// we need the `Any` object anyway, we're not just creating it to
370 /// avoid being generic.)
371 ///
372 /// Do this split took the LLVM IR line counts of `fn main() { fail!()
373 /// }` from ~1900/3700 (-O/no opts) to 180/590.
374 #[inline(never)] #[cold] // this is the slow path, please never inline this
375 fn begin_unwind_inner(msg: ~Any, file: &'static str, line: uint) -> ! {
376     let mut task;
377     {
378         let msg_s = match msg.as_ref::<&'static str>() {
379             Some(s) => *s,
380             None => match msg.as_ref::<~str>() {
381                 Some(s) => s.as_slice(),
382                 None => "~Any",
383             }
384         };
385
386         // It is assumed that all reasonable rust code will have a local task at
387         // all times. This means that this `try_take` will succeed almost all of
388         // the time. There are border cases, however, when the runtime has
389         // *almost* set up the local task, but hasn't quite gotten there yet. In
390         // order to get some better diagnostics, we print on failure and
391         // immediately abort the whole process if there is no local task
392         // available.
393         let opt_task: Option<~Task> = Local::try_take();
394         task = match opt_task {
395             Some(t) => t,
396             None => {
397                 rterrln!("failed at '{}', {}:{}", msg_s, file, line);
398                 if backtrace::log_enabled() {
399                     let mut err = ::rt::util::Stderr;
400                     let _err = backtrace::write(&mut err);
401                 }
402                 unsafe { intrinsics::abort() }
403             }
404         };
405
406         // See comments in io::stdio::with_task_stdout as to why we have to be
407         // careful when using an arbitrary I/O handle from the task. We
408         // essentially need to dance to make sure when a task is in TLS when
409         // running user code.
410         let name = task.name.take();
411         {
412             let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
413
414             match task.stderr.take() {
415                 Some(mut stderr) => {
416                     Local::put(task);
417                     // FIXME: what to do when the task printing fails?
418                     let _err = format_args!(|args| ::fmt::writeln(stderr, args),
419                                             "task '{}' failed at '{}', {}:{}",
420                                             n, msg_s, file, line);
421                     if backtrace::log_enabled() {
422                         let _err = backtrace::write(stderr);
423                     }
424                     task = Local::take();
425
426                     match mem::replace(&mut task.stderr, Some(stderr)) {
427                         Some(prev) => {
428                             Local::put(task);
429                             drop(prev);
430                             task = Local::take();
431                         }
432                         None => {}
433                     }
434                 }
435                 None => {
436                     rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
437                              file, line);
438                     if backtrace::log_enabled() {
439                         let mut err = ::rt::util::Stderr;
440                         let _err = backtrace::write(&mut err);
441                     }
442                 }
443             }
444         }
445         task.name = name;
446
447         if task.unwinder.unwinding {
448             // If a task fails while it's already unwinding then we
449             // have limited options. Currently our preference is to
450             // just abort. In the future we may consider resuming
451             // unwinding or otherwise exiting the task cleanly.
452             rterrln!("task failed during unwinding (double-failure - total drag!)")
453             rterrln!("rust must abort now. so sorry.");
454
455             // Don't print the backtrace twice (it would have already been
456             // printed if logging was enabled).
457             if !backtrace::log_enabled() {
458                 let mut err = ::rt::util::Stderr;
459                 let _err = backtrace::write(&mut err);
460             }
461             unsafe { intrinsics::abort() }
462         }
463     }
464
465     // The unwinder won't actually use the task at all, so we put the task back
466     // into TLS right before we invoke the unwinder, but this means we need an
467     // unsafe reference back to the unwinder once it's in TLS.
468     Local::put(task);
469     unsafe {
470         let task: *mut Task = Local::unsafe_borrow();
471         (*task).unwinder.begin_unwind(msg);
472     }
473 }