]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / sys / unix / backtrace / tracing / gcc_s.rs
CommitLineData
e9174d1e
SL
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.
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
11use io;
12use io::prelude::*;
13use libc;
14use mem;
15use sync::StaticMutex;
16
17use super::super::printing::print;
18
19#[inline(never)] // if we know this is a function call, we can skip it when
20 // tracing
21pub fn write(w: &mut Write) -> io::Result<()> {
22 struct Context<'a> {
23 idx: isize,
24 writer: &'a mut (Write+'a),
25 last_error: Option<io::Error>,
26 }
27
28 // When using libbacktrace, we use some necessary global state, so we
29 // need to prevent more than one thread from entering this block. This
30 // is semi-reasonable in terms of printing anyway, and we know that all
31 // I/O done here is blocking I/O, not green I/O, so we don't have to
32 // worry about this being a native vs green mutex.
33 static LOCK: StaticMutex = StaticMutex::new();
34 let _g = LOCK.lock();
35
54a0048b 36 writeln!(w, "stack backtrace:")?;
e9174d1e
SL
37
38 let mut cx = Context { writer: w, last_error: None, idx: 0 };
39 return match unsafe {
40 uw::_Unwind_Backtrace(trace_fn,
41 &mut cx as *mut Context as *mut libc::c_void)
42 } {
43 uw::_URC_NO_REASON => {
44 match cx.last_error {
45 Some(err) => Err(err),
46 None => Ok(())
47 }
48 }
49 _ => Ok(()),
50 };
51
52 extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
53 arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
54 let cx: &mut Context = unsafe { mem::transmute(arg) };
55 let mut ip_before_insn = 0;
56 let mut ip = unsafe {
57 uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
58 };
59 if !ip.is_null() && ip_before_insn == 0 {
60 // this is a non-signaling frame, so `ip` refers to the address
61 // after the calling instruction. account for that.
62 ip = (ip as usize - 1) as *mut _;
63 }
64
65 // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
66 // it appears to work fine without it, so we only use
67 // FindEnclosingFunction on non-osx platforms. In doing so, we get a
68 // slightly more accurate stack trace in the process.
69 //
70 // This is often because panic involves the last instruction of a
71 // function being "call std::rt::begin_unwind", with no ret
72 // instructions after it. This means that the return instruction
73 // pointer points *outside* of the calling function, and by
74 // unwinding it we go back to the original function.
75 let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
76 ip
77 } else {
78 unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
79 };
80
81 // Don't print out the first few frames (they're not user frames)
82 cx.idx += 1;
83 if cx.idx <= 0 { return uw::_URC_NO_REASON }
84 // Don't print ginormous backtraces
85 if cx.idx > 100 {
86 match write!(cx.writer, " ... <frames omitted>\n") {
87 Ok(()) => {}
88 Err(e) => { cx.last_error = Some(e); }
89 }
90 return uw::_URC_FAILURE
91 }
92
93 // Once we hit an error, stop trying to print more frames
94 if cx.last_error.is_some() { return uw::_URC_FAILURE }
95
96 match print(cx.writer, cx.idx, ip, symaddr) {
97 Ok(()) => {}
98 Err(e) => { cx.last_error = Some(e); }
99 }
100
101 // keep going
102 uw::_URC_NO_REASON
103 }
104}
105
106/// Unwind library interface used for backtraces
107///
108/// Note that dead code is allowed as here are just bindings
109/// iOS doesn't use all of them it but adding more
110/// platform-specific configs pollutes the code too much
111#[allow(non_camel_case_types)]
112#[allow(non_snake_case)]
113mod uw {
114 pub use self::_Unwind_Reason_Code::*;
115
116 use libc;
117
118 #[repr(C)]
119 pub enum _Unwind_Reason_Code {
120 _URC_NO_REASON = 0,
121 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
122 _URC_FATAL_PHASE2_ERROR = 2,
123 _URC_FATAL_PHASE1_ERROR = 3,
124 _URC_NORMAL_STOP = 4,
125 _URC_END_OF_STACK = 5,
126 _URC_HANDLER_FOUND = 6,
127 _URC_INSTALL_CONTEXT = 7,
128 _URC_CONTINUE_UNWIND = 8,
129 _URC_FAILURE = 9, // used only by ARM EABI
130 }
131
132 pub enum _Unwind_Context {}
133
134 pub type _Unwind_Trace_Fn =
135 extern fn(ctx: *mut _Unwind_Context,
136 arg: *mut libc::c_void) -> _Unwind_Reason_Code;
137
138 extern {
139 // No native _Unwind_Backtrace on iOS
140 #[cfg(not(all(target_os = "ios", target_arch = "arm")))]
141 pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
142 trace_argument: *mut libc::c_void)
143 -> _Unwind_Reason_Code;
144
145 // available since GCC 4.2.0, should be fine for our purpose
146 #[cfg(all(not(all(target_os = "android", target_arch = "arm")),
147 not(all(target_os = "linux", target_arch = "arm"))))]
148 pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
149 ip_before_insn: *mut libc::c_int)
150 -> libc::uintptr_t;
151
152 #[cfg(all(not(target_os = "android"),
153 not(all(target_os = "linux", target_arch = "arm"))))]
154 pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
155 -> *mut libc::c_void;
156 }
157
158 // On android, the function _Unwind_GetIP is a macro, and this is the
159 // expansion of the macro. This is all copy/pasted directly from the
160 // header file with the definition of _Unwind_GetIP.
161 #[cfg(any(all(target_os = "android", target_arch = "arm"),
162 all(target_os = "linux", target_arch = "arm")))]
163 pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
164 #[repr(C)]
165 enum _Unwind_VRS_Result {
166 _UVRSR_OK = 0,
167 _UVRSR_NOT_IMPLEMENTED = 1,
168 _UVRSR_FAILED = 2,
169 }
170 #[repr(C)]
171 enum _Unwind_VRS_RegClass {
172 _UVRSC_CORE = 0,
173 _UVRSC_VFP = 1,
174 _UVRSC_FPA = 2,
175 _UVRSC_WMMXD = 3,
176 _UVRSC_WMMXC = 4,
177 }
178 #[repr(C)]
179 enum _Unwind_VRS_DataRepresentation {
180 _UVRSD_UINT32 = 0,
181 _UVRSD_VFPX = 1,
182 _UVRSD_FPAX = 2,
183 _UVRSD_UINT64 = 3,
184 _UVRSD_FLOAT = 4,
185 _UVRSD_DOUBLE = 5,
186 }
187
188 type _Unwind_Word = libc::c_uint;
189 extern {
190 fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
191 klass: _Unwind_VRS_RegClass,
192 word: _Unwind_Word,
193 repr: _Unwind_VRS_DataRepresentation,
194 data: *mut libc::c_void)
195 -> _Unwind_VRS_Result;
196 }
197
198 let mut val: _Unwind_Word = 0;
199 let ptr = &mut val as *mut _Unwind_Word;
200 let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15,
201 _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
202 ptr as *mut libc::c_void);
203 (val & !1) as libc::uintptr_t
204 }
205
206 // This function doesn't exist on Android or ARM/Linux, so make it same
207 // to _Unwind_GetIP
208 #[cfg(any(all(target_os = "android", target_arch = "arm"),
209 all(target_os = "linux", target_arch = "arm")))]
210 pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
211 ip_before_insn: *mut libc::c_int)
212 -> libc::uintptr_t
213 {
214 *ip_before_insn = 0;
215 _Unwind_GetIP(ctx)
216 }
217
218 // This function also doesn't exist on Android or ARM/Linux, so make it
219 // a no-op
220 #[cfg(any(target_os = "android",
221 all(target_os = "linux", target_arch = "arm")))]
222 pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void)
223 -> *mut libc::c_void
224 {
225 pc
226 }
227}