Skip to content

Commit cf79540

Browse files
committed
Add basic backtrace functionality
Whenever a failure happens, if a program is run with `RUST_LOG=std::rt::backtrace` a backtrace will be printed to the task's stderr handle. Currently this is not an exported interface because it's only supported on osx/linux. I found that windows/freebsd/android all required extra libraries which are not necessarily present by default. cc rust-lang#10128
1 parent 6aad3bf commit cf79540

File tree

4 files changed

+197
-11
lines changed

4 files changed

+197
-11
lines changed

src/libstd/rt/backtrace.rs

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright 2014 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+
#[allow(non_camel_case_types)];
12+
13+
use c_str::CString;
14+
use container::Container;
15+
use io::{Writer, IoResult};
16+
use iter::{range, Iterator};
17+
use libc;
18+
use option::{Some, None};
19+
use result::{Ok, Err};
20+
use unstable::raw::{Repr, Slice};
21+
use vec::ImmutableVector;
22+
23+
static MAX_BACKTRACE: uint = 128;
24+
25+
pub fn log_enabled() -> bool {
26+
log_enabled!(::logging::DEBUG)
27+
}
28+
29+
#[cfg(target_os = "macos")]
30+
#[cfg(target_os = "linux")]
31+
pub fn write(w: &mut Writer) -> IoResult<()> {
32+
use ptr::RawPtr;
33+
use ptr;
34+
35+
extern {
36+
fn backtrace(array: *mut *mut libc::c_void,
37+
size: libc::c_int) -> libc::c_int;
38+
fn backtrace_symbols(array: *mut *libc::c_void,
39+
size: libc::c_int) -> **libc::c_char;
40+
}
41+
42+
let array = [0u, ..MAX_BACKTRACE];
43+
let Slice { data, len } = array.repr();
44+
let len = unsafe {
45+
backtrace(data as *mut *mut libc::c_void, len as libc::c_int)
46+
};
47+
if len == 0 { return Ok(()) }
48+
49+
let arr = unsafe {
50+
backtrace_symbols(data as *mut *libc::c_void, len)
51+
};
52+
if arr.is_null() { return Ok(()) }
53+
54+
if_ok!(write!(w, "stack backtrace:\n"));
55+
for i in range(0, len) {
56+
let c_str = unsafe { CString::new(*ptr::offset(arr, i as int), false) };
57+
if_ok!(w.write_str(" "));
58+
let bytes = c_str.as_bytes();
59+
if_ok!(w.write(bytes.slice_to(bytes.len() - 1)));
60+
if_ok!(w.write_str("\n"));
61+
}
62+
63+
Ok(())
64+
}
65+
66+
// Windows uses functions from DbgHelp.dll which looks like it's not installed
67+
// on mingw by default. Right now we don't have a great way of conditionally
68+
// linking to something based on is presence, so for now we just ignore
69+
// windows.
70+
//
71+
// The commented out code below is an untested implementation (but compiles) as
72+
// I could never find DbgHelp.dll...
73+
#[cfg(target_os = "win32")]
74+
fn write(_: &mut Writer) -> IoResult<()> { Ok(()) }
75+
/*
76+
#[cfg(target_os = "win32")]
77+
pub fn write(w: &mut Writer) -> IoResult<()> {
78+
use mem;
79+
use unstable::intrinsics;
80+
81+
struct SYMBOL_INFO {
82+
SizeOfStruct: libc::c_ulong,
83+
TypeIndex: libc::c_ulong,
84+
Reserved: [u64, ..2],
85+
Index: libc::c_ulong,
86+
Size: libc::c_ulong,
87+
ModBase: u64,
88+
Flags: libc::c_ulong,
89+
Value: u64,
90+
Address: u64,
91+
Register: libc::c_ulong,
92+
Scope: libc::c_ulong,
93+
Tag: libc::c_ulong,
94+
NameLen: libc::c_ulong,
95+
MaxNameLen: libc::c_ulong,
96+
// note that windows has this as 1, but it basically just means that the
97+
// name is inline at the end of the struct. For us, we just bump the
98+
// struct size up to 256.
99+
Name: [libc::c_char, ..256],
100+
}
101+
102+
#[link(name = "DbgHelp")]
103+
extern "system" {
104+
fn CaptureStackBackTrace(
105+
FramesToSkip: libc::c_ulong,
106+
FramesToCapture: libc::c_ulong,
107+
BackTrace: *mut *libc::c_void,
108+
BackTraceHash: *libc::c_ulong,
109+
) -> libc::c_short;
110+
111+
fn SymFromAddr(
112+
hProcess: libc::HANDLE,
113+
Address: i64,
114+
Displacement: *i64,
115+
Symbol: *mut SYMBOL_INFO,
116+
) -> libc::BOOL;
117+
118+
fn GetCurrentProcess() -> libc::HANDLE;
119+
fn SymInitialize(
120+
hProcess: libc::HANDLE,
121+
UserSearchPath: *libc::c_void,
122+
fInvadeProcess: libc::BOOL,
123+
) -> libc::BOOL;
124+
}
125+
126+
let process = unsafe { GetCurrentProcess() };
127+
let ret = unsafe { SymInitialize(process, 0 as *libc::c_void, libc::TRUE) };
128+
if ret != libc::TRUE { return Ok(()) }
129+
130+
let array = [0u, ..MAX_BACKTRACE];
131+
let Slice { data, len } = array.repr();
132+
let len = unsafe {
133+
CaptureStackBackTrace(0, len as libc::c_ulong,
134+
data as *mut *libc::c_void, 0 as *libc::c_ulong)
135+
};
136+
if len == 0 { return Ok(()) }
137+
138+
if_ok!(write!(w, "stack backtrace:\n"));
139+
let mut info: SYMBOL_INFO = unsafe { intrinsics::init() };
140+
for i in range(0, len) {
141+
info.MaxNameLen = (info.Name.len() - 1) as libc::c_ulong;
142+
info.SizeOfStruct = (mem::size_of::<SYMBOL_INFO>() -
143+
info.Name.len() + 1) as libc::c_ulong;
144+
145+
let ret = unsafe {
146+
SymFromAddr(process, array[i] as i64, 0 as *i64, &mut info)
147+
};
148+
149+
if ret == libc::TRUE {
150+
let cstr = unsafe { CString::new(info.Name.as_ptr(), false) };
151+
if_ok!(match cstr.as_str() {
152+
Some(s) => writeln!(w, "{}: {} - {:#10x}", i, s, info.Address),
153+
None => writeln!(w, "{}: <not-utf8> - {:#10x}", i, info.Address)
154+
})
155+
} else {
156+
if_ok!(w.write_str(" <SymFromAddr failed>\n"));
157+
}
158+
}
159+
160+
Ok(())
161+
}
162+
*/
163+
164+
165+
// Apparently freebsd has its backtrace functionality through an external
166+
// libexecinfo library (not on the system by default). For now, just ignore
167+
// writing backtraces on freebsd
168+
#[cfg(target_os = "freebsd")]
169+
fn write(_: &mut Writer) -> IoResult<()> { Ok(()) }
170+
171+
// Android, like freebsd, I believe also uses libexecinfo as a separate library.
172+
// For now this is just a stub.
173+
#[cfg(target_os = "android")]
174+
fn write(_: &mut Writer) -> IoResult<()> { Ok(()) }

src/libstd/rt/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ mod thread_local_storage;
119119
/// Stack unwinding
120120
pub mod unwind;
121121

122+
/// Simple backtrace functionality (to print on failure)
123+
mod backtrace;
124+
122125
/// Just stuff
123126
mod util;
124127

src/libstd/rt/unwind.rs

+8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use option::{Some, None, Option};
6464
use prelude::drop;
6565
use ptr::RawPtr;
6666
use result::{Err, Ok};
67+
use rt::backtrace;
6768
use rt::local::Local;
6869
use rt::task::Task;
6970
use str::Str;
@@ -468,6 +469,9 @@ fn begin_unwind_inner(msg: ~Any, file: &'static str, line: uint) -> ! {
468469
let _err = format_args!(|args| ::fmt::writeln(stderr, args),
469470
"task '{}' failed at '{}', {}:{}",
470471
n, msg_s, file, line);
472+
if backtrace::log_enabled() {
473+
let _err = backtrace::write(stderr);
474+
}
471475
task = Local::take();
472476

473477
match util::replace(&mut task.stderr, Some(stderr)) {
@@ -482,6 +486,10 @@ fn begin_unwind_inner(msg: ~Any, file: &'static str, line: uint) -> ! {
482486
None => {
483487
rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
484488
file, line);
489+
if backtrace::log_enabled() {
490+
let mut err = ::rt::util::Stderr;
491+
let _err = backtrace::write(&mut err);
492+
}
485493
}
486494
}
487495
}

src/libstd/rt/util.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use container::Container;
1212
use fmt;
1313
use from_str::FromStr;
1414
use io::IoResult;
15+
use io;
1516
use iter::Iterator;
1617
use libc;
1718
use option::{Some, None, Option};
@@ -70,20 +71,20 @@ pub fn default_sched_threads() -> uint {
7071
}
7172
}
7273

73-
pub fn dumb_println(args: &fmt::Arguments) {
74-
use io;
74+
pub struct Stderr;
7575

76-
struct Stderr;
77-
impl io::Writer for Stderr {
78-
fn write(&mut self, data: &[u8]) -> IoResult<()> {
79-
unsafe {
80-
libc::write(libc::STDERR_FILENO,
81-
data.as_ptr() as *libc::c_void,
82-
data.len() as libc::size_t);
83-
}
84-
Ok(()) // yes, we're lying
76+
impl io::Writer for Stderr {
77+
fn write(&mut self, data: &[u8]) -> IoResult<()> {
78+
unsafe {
79+
libc::write(libc::STDERR_FILENO,
80+
data.as_ptr() as *libc::c_void,
81+
data.len() as libc::size_t);
8582
}
83+
Ok(()) // yes, we're lying
8684
}
85+
}
86+
87+
pub fn dumb_println(args: &fmt::Arguments) {
8788
let mut w = Stderr;
8889
let _ = fmt::writeln(&mut w as &mut io::Writer, args);
8990
}

0 commit comments

Comments
 (0)