]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/common/gnu/libbacktrace.rs
Imported Upstream version 1.6.0+dfsg1
[rustc.git] / src / libstd / sys / common / gnu / libbacktrace.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 sys_common::backtrace::{output, output_fileline};
15
16pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
17 symaddr: *mut libc::c_void) -> io::Result<()> {
18 use env;
19 use ffi::CStr;
20 use ptr;
21
22 ////////////////////////////////////////////////////////////////////////
23 // libbacktrace.h API
24 ////////////////////////////////////////////////////////////////////////
25 type backtrace_syminfo_callback =
26 extern "C" fn(data: *mut libc::c_void,
27 pc: libc::uintptr_t,
28 symname: *const libc::c_char,
29 symval: libc::uintptr_t,
30 symsize: libc::uintptr_t);
31 type backtrace_full_callback =
32 extern "C" fn(data: *mut libc::c_void,
33 pc: libc::uintptr_t,
34 filename: *const libc::c_char,
35 lineno: libc::c_int,
36 function: *const libc::c_char) -> libc::c_int;
37 type backtrace_error_callback =
38 extern "C" fn(data: *mut libc::c_void,
39 msg: *const libc::c_char,
40 errnum: libc::c_int);
41 enum backtrace_state {}
42 #[link(name = "backtrace", kind = "static")]
43 #[cfg(not(test))]
44 extern {}
45
46 extern {
47 fn backtrace_create_state(filename: *const libc::c_char,
48 threaded: libc::c_int,
49 error: backtrace_error_callback,
50 data: *mut libc::c_void)
51 -> *mut backtrace_state;
52 fn backtrace_syminfo(state: *mut backtrace_state,
53 addr: libc::uintptr_t,
54 cb: backtrace_syminfo_callback,
55 error: backtrace_error_callback,
56 data: *mut libc::c_void) -> libc::c_int;
57 fn backtrace_pcinfo(state: *mut backtrace_state,
58 addr: libc::uintptr_t,
59 cb: backtrace_full_callback,
60 error: backtrace_error_callback,
61 data: *mut libc::c_void) -> libc::c_int;
62 }
63
64 ////////////////////////////////////////////////////////////////////////
65 // helper callbacks
66 ////////////////////////////////////////////////////////////////////////
67
68 type FileLine = (*const libc::c_char, libc::c_int);
69
70 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
71 _errnum: libc::c_int) {
72 // do nothing for now
73 }
74 extern fn syminfo_cb(data: *mut libc::c_void,
75 _pc: libc::uintptr_t,
76 symname: *const libc::c_char,
77 _symval: libc::uintptr_t,
78 _symsize: libc::uintptr_t) {
79 let slot = data as *mut *const libc::c_char;
80 unsafe { *slot = symname; }
81 }
82 extern fn pcinfo_cb(data: *mut libc::c_void,
83 _pc: libc::uintptr_t,
84 filename: *const libc::c_char,
85 lineno: libc::c_int,
86 _function: *const libc::c_char) -> libc::c_int {
87 if !filename.is_null() {
88 let slot = data as *mut &mut [FileLine];
89 let buffer = unsafe {ptr::read(slot)};
90
91 // if the buffer is not full, add file:line to the buffer
92 // and adjust the buffer for next possible calls to pcinfo_cb.
93 if !buffer.is_empty() {
94 buffer[0] = (filename, lineno);
95 unsafe { ptr::write(slot, &mut buffer[1..]); }
96 }
97 }
98
99 0
100 }
101
102 // The libbacktrace API supports creating a state, but it does not
103 // support destroying a state. I personally take this to mean that a
104 // state is meant to be created and then live forever.
105 //
106 // I would love to register an at_exit() handler which cleans up this
107 // state, but libbacktrace provides no way to do so.
108 //
109 // With these constraints, this function has a statically cached state
110 // that is calculated the first time this is requested. Remember that
111 // backtracing all happens serially (one global lock).
112 //
113 // An additionally oddity in this function is that we initialize the
114 // filename via self_exe_name() to pass to libbacktrace. It turns out
115 // that on Linux libbacktrace seamlessly gets the filename of the
116 // current executable, but this fails on freebsd. by always providing
117 // it, we make sure that libbacktrace never has a reason to not look up
118 // the symbols. The libbacktrace API also states that the filename must
119 // be in "permanent memory", so we copy it to a static and then use the
120 // static as the pointer.
121 //
122 // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't
123 // tested if this is required or not.
124 unsafe fn init_state() -> *mut backtrace_state {
125 static mut STATE: *mut backtrace_state = ptr::null_mut();
126 static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256];
127 if !STATE.is_null() { return STATE }
128 let selfname = if cfg!(target_os = "freebsd") ||
129 cfg!(target_os = "dragonfly") ||
130 cfg!(target_os = "bitrig") ||
131 cfg!(target_os = "openbsd") ||
132 cfg!(target_os = "windows") {
133 env::current_exe().ok()
134 } else {
135 None
136 };
92a42be0 137 let filename = match selfname.as_ref().and_then(|s| s.to_str()) {
e9174d1e 138 Some(path) => {
92a42be0 139 let bytes = path.as_bytes();
e9174d1e
SL
140 if bytes.len() < LAST_FILENAME.len() {
141 let i = bytes.iter();
142 for (slot, val) in LAST_FILENAME.iter_mut().zip(i) {
143 *slot = *val as libc::c_char;
144 }
145 LAST_FILENAME.as_ptr()
146 } else {
147 ptr::null()
148 }
149 }
150 None => ptr::null(),
151 };
152 STATE = backtrace_create_state(filename, 0, error_cb,
153 ptr::null_mut());
154 STATE
155 }
156
157 ////////////////////////////////////////////////////////////////////////
158 // translation
159 ////////////////////////////////////////////////////////////////////////
160
161 // backtrace errors are currently swept under the rug, only I/O
162 // errors are reported
163 let state = unsafe { init_state() };
164 if state.is_null() {
165 return output(w, idx, addr, None)
166 }
167 let mut data = ptr::null();
168 let data_addr = &mut data as *mut *const libc::c_char;
169 let ret = unsafe {
170 backtrace_syminfo(state, symaddr as libc::uintptr_t,
171 syminfo_cb, error_cb,
172 data_addr as *mut libc::c_void)
173 };
174 if ret == 0 || data.is_null() {
175 try!(output(w, idx, addr, None));
176 } else {
177 try!(output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() })));
178 }
179
180 // pcinfo may return an arbitrary number of file:line pairs,
181 // in the order of stack trace (i.e. inlined calls first).
182 // in order to avoid allocation, we stack-allocate a fixed size of entries.
183 const FILELINE_SIZE: usize = 32;
184 let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
185 let ret;
186 let fileline_count;
187 {
188 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
189 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
190 ret = unsafe {
191 backtrace_pcinfo(state, addr as libc::uintptr_t,
192 pcinfo_cb, error_cb,
193 fileline_addr as *mut libc::c_void)
194 };
195 fileline_count = FILELINE_SIZE - fileline_win.len();
196 }
197 if ret == 0 {
198 for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
199 if file.is_null() { continue; } // just to be sure
200 let file = unsafe { CStr::from_ptr(file).to_bytes() };
201 try!(output_fileline(w, file, line, i == FILELINE_SIZE - 1));
202 }
203 }
204
205 Ok(())
206}