1 // Copyright 2014-2015 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.
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.
11 //! Backtrace support using libunwind/gcc_s/etc APIs.
13 //! This module contains the ability to unwind the stack using libunwind-style
14 //! APIs. Note that there's a whole bunch of implementations of the
15 //! libunwind-like API, and this is just trying to be compatible with most of
16 //! them all at once instead of being picky.
18 //! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
19 //! reliable at generating a backtrace. It's not entirely clear how it does it
20 //! (frame pointers? eh_frame info? both?) but it seems to work!
22 //! Most of the complexity of this module is handling the various platform
23 //! differences across libunwind implementations. Otherwise this is a pretty
24 //! straightforward Rust binding to the libunwind APIs.
26 //! This is the default unwinding API for all non-Windows platforms currently.
28 use core
::ffi
::c_void
;
31 Raw(*mut uw
::_Unwind_Context
),
34 symbol_address
: *mut c_void
,
38 // With a raw libunwind pointer it should only ever be access in a readonly
39 // threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
40 // we always switch to a version which doesn't retain interior pointers, so we
41 // should be `Send` as well.
42 unsafe impl Send
for Frame {}
43 unsafe impl Sync
for Frame {}
46 pub fn ip(&self) -> *mut c_void
{
47 let ctx
= match *self {
48 Frame
::Raw(ctx
) => ctx
,
49 Frame
::Cloned { ip, .. }
=> return ip
,
52 uw
::_Unwind_GetIP(ctx
) as *mut c_void
56 pub fn symbol_address(&self) -> *mut c_void
{
57 if let Frame
::Cloned { symbol_address, .. }
= *self {
58 return symbol_address
;
61 // It seems that on OSX `_Unwind_FindEnclosingFunction` returns a
62 // pointer to... something that's unclear. It's definitely not always
63 // the enclosing function for whatever reason. It's not entirely clear
64 // to me what's going on here, so pessimize this for now and just always
67 // Note the `skip_inner_frames.rs` test is skipped on OSX due to this
68 // clause, and if this is fixed that test in theory can be run on OSX!
69 if cfg
!(target_os
= "macos") || cfg
!(target_os
= "ios") {
72 unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
77 impl Clone
for Frame
{
78 fn clone(&self) -> Frame
{
81 symbol_address
: self.symbol_address(),
87 pub unsafe fn trace(mut cb
: &mut FnMut(&super::Frame
) -> bool
) {
88 uw
::_Unwind_Backtrace(trace_fn
, &mut cb
as *mut _
as *mut _
);
90 extern fn trace_fn(ctx
: *mut uw
::_Unwind_Context
,
91 arg
: *mut c_void
) -> uw
::_Unwind_Reason_Code
{
92 let cb
= unsafe { &mut *(arg as *mut &mut FnMut(&super::Frame) -> bool) }
;
93 let cx
= super::Frame
{
94 inner
: Frame
::Raw(ctx
),
97 let mut bomb
= crate::Bomb { enabled: true }
;
98 let keep_going
= cb(&cx
);
109 /// Unwind library interface used for backtraces
111 /// Note that dead code is allowed as here are just bindings
112 /// iOS doesn't use all of them it but adding more
113 /// platform-specific configs pollutes the code too much
114 #[allow(non_camel_case_types)]
115 #[allow(non_snake_case)]
118 pub use self::_Unwind_Reason_Code
::*;
120 use core
::ffi
::c_void
;
123 pub enum _Unwind_Reason_Code
{
125 _URC_FOREIGN_EXCEPTION_CAUGHT
= 1,
126 _URC_FATAL_PHASE2_ERROR
= 2,
127 _URC_FATAL_PHASE1_ERROR
= 3,
128 _URC_NORMAL_STOP
= 4,
129 _URC_END_OF_STACK
= 5,
130 _URC_HANDLER_FOUND
= 6,
131 _URC_INSTALL_CONTEXT
= 7,
132 _URC_CONTINUE_UNWIND
= 8,
133 _URC_FAILURE
= 9, // used only by ARM EABI
136 pub enum _Unwind_Context {}
138 pub type _Unwind_Trace_Fn
=
139 extern fn(ctx
: *mut _Unwind_Context
,
140 arg
: *mut c_void
) -> _Unwind_Reason_Code
;
143 // No native _Unwind_Backtrace on iOS
144 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
145 pub fn _Unwind_Backtrace(trace
: _Unwind_Trace_Fn
,
146 trace_argument
: *mut c_void
)
147 -> _Unwind_Reason_Code
;
149 // available since GCC 4.2.0, should be fine for our purpose
150 #[cfg(all(not(all(target_os = "android", target_arch = "arm")),
151 not(all(target_os
= "freebsd", target_arch
= "arm")),
152 not(all(target_os
= "linux", target_arch
= "arm"))))]
153 pub fn _Unwind_GetIP(ctx
: *mut _Unwind_Context
)
156 #[cfg(all(not(target_os = "android"),
157 not(all(target_os
= "freebsd", target_arch
= "arm")),
158 not(all(target_os
= "linux", target_arch
= "arm"))))]
159 pub fn _Unwind_FindEnclosingFunction(pc
: *mut c_void
)
163 // On android, the function _Unwind_GetIP is a macro, and this is the
164 // expansion of the macro. This is all copy/pasted directly from the
165 // header file with the definition of _Unwind_GetIP.
166 #[cfg(any(all(target_os = "android", target_arch = "arm"),
167 all(target_os
= "freebsd", target_arch
= "arm"),
168 all(target_os
= "linux", target_arch
= "arm")))]
169 pub unsafe fn _Unwind_GetIP(ctx
: *mut _Unwind_Context
) -> libc
::uintptr_t
{
171 enum _Unwind_VRS_Result
{
173 _UVRSR_NOT_IMPLEMENTED
= 1,
177 enum _Unwind_VRS_RegClass
{
185 enum _Unwind_VRS_DataRepresentation
{
194 type _Unwind_Word
= libc
::c_uint
;
196 fn _Unwind_VRS_Get(ctx
: *mut _Unwind_Context
,
197 klass
: _Unwind_VRS_RegClass
,
199 repr
: _Unwind_VRS_DataRepresentation
,
201 -> _Unwind_VRS_Result
;
204 let mut val
: _Unwind_Word
= 0;
205 let ptr
= &mut val
as *mut _Unwind_Word
;
206 let _
= _Unwind_VRS_Get(ctx
, _Unwind_VRS_RegClass
::_UVRSC_CORE
, 15,
207 _Unwind_VRS_DataRepresentation
::_UVRSD_UINT32
,
209 (val
& !1) as libc
::uintptr_t
212 // This function also doesn't exist on Android or ARM/Linux, so make it
214 #[cfg(any(target_os = "android",
215 all(target_os
= "freebsd", target_arch
= "arm"),
216 all(target_os
= "linux", target_arch
= "arm")))]
217 pub unsafe fn _Unwind_FindEnclosingFunction(pc
: *mut c_void
)