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.
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 #![cfg_attr(target_os = "nacl", allow(dead_code))]
13 /// Common code for printing the backtrace in the same way across the different
14 /// supported platforms.
21 use sync
::atomic
::{self, Ordering}
;
22 use path
::{self, Path}
;
23 use sys
::mutex
::Mutex
;
26 pub use sys
::backtrace
::{
29 foreach_symbol_fileline
,
33 #[cfg(target_pointer_width = "64")]
34 pub const HEX_WIDTH
: usize = 18;
36 #[cfg(target_pointer_width = "32")]
37 pub const HEX_WIDTH
: usize = 10;
39 /// Represents an item in the backtrace list. See `unwind_backtrace` for how
41 #[derive(Debug, Copy, Clone)]
43 /// Exact address of the call that failed.
44 pub exact_position
: *const libc
::c_void
,
45 /// Address of the enclosing function.
46 pub symbol_addr
: *const libc
::c_void
,
49 /// Max number of frames to print.
50 const MAX_NB_FRAMES
: usize = 100;
52 /// Prints the current backtrace.
53 pub fn print(w
: &mut Write
, format
: PrintFormat
) -> io
::Result
<()> {
54 static LOCK
: Mutex
= Mutex
::new();
56 // Use a lock to prevent mixed output in multithreading context.
57 // Some platforms also requires it, like `SymFromAddr` on Windows.
60 let res
= _print(w
, format
);
66 fn _print(w
: &mut Write
, format
: PrintFormat
) -> io
::Result
<()> {
67 let mut frames
= [Frame
{
68 exact_position
: ptr
::null(),
69 symbol_addr
: ptr
::null(),
71 let (nb_frames
, context
) = unwind_backtrace(&mut frames
)?
;
72 let (skipped_before
, skipped_after
) =
73 filter_frames(&frames
[..nb_frames
], format
, &context
);
74 if skipped_before
+ skipped_after
> 0 {
75 writeln
!(w
, "note: Some details are omitted, \
76 run with `RUST_BACKTRACE=full` for a verbose backtrace.")?
;
78 writeln
!(w
, "stack backtrace:")?
;
80 let filtered_frames
= &frames
[..nb_frames
- skipped_after
];
81 for (index
, frame
) in filtered_frames
.iter().skip(skipped_before
).enumerate() {
82 resolve_symname(*frame
, |symname
| {
83 output(w
, index
, *frame
, symname
, format
)
85 let has_more_filenames
= foreach_symbol_fileline(*frame
, |file
, line
| {
86 output_fileline(w
, file
, line
, format
)
88 if has_more_filenames
{
89 w
.write_all(b
" <... and possibly more>")?
;
96 /// Returns a number of frames to remove at the beginning and at the end of the
97 /// backtrace, according to the backtrace format.
98 fn filter_frames(frames
: &[Frame
],
100 context
: &BacktraceContext
) -> (usize, usize)
102 if format
== PrintFormat
::Full
{
106 let skipped_before
= 0;
108 let skipped_after
= frames
.len() - frames
.iter().position(|frame
| {
109 let mut is_marker
= false;
110 let _
= resolve_symname(*frame
, |symname
| {
111 if let Some(mangled_symbol_name
) = symname
{
112 // Use grep to find the concerned functions
113 if mangled_symbol_name
.contains("__rust_begin_short_backtrace") {
120 }).unwrap_or(frames
.len());
122 if skipped_before
+ skipped_after
>= frames
.len() {
123 // Avoid showing completely empty backtraces
127 (skipped_before
, skipped_after
)
131 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
133 pub fn __rust_begin_short_backtrace
<F
, T
>(f
: F
) -> T
134 where F
: FnOnce() -> T
, F
: Send
+ '
static, T
: Send
+ '
static
139 /// Controls how the backtrace should be formated.
140 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
141 pub enum PrintFormat
{
142 /// Show all the frames with absolute path for files.
144 /// Show only relevant data from the backtrace.
148 // For now logging is turned off by default, and this function checks to see
149 // whether the magical environment variable is present to see if it's turned on.
150 pub fn log_enabled() -> Option
<PrintFormat
> {
151 static ENABLED
: atomic
::AtomicIsize
= atomic
::AtomicIsize
::new(0);
152 match ENABLED
.load(Ordering
::SeqCst
) {
155 2 => return Some(PrintFormat
::Full
),
156 3 => return Some(PrintFormat
::Short
),
160 let val
= match env
::var_os("RUST_BACKTRACE") {
161 Some(x
) => if &x
== "0" {
163 } else if &x
== "full" {
164 Some(PrintFormat
::Full
)
166 Some(PrintFormat
::Short
)
170 ENABLED
.store(match val
{
171 Some(v
) => v
as isize,
173 }, Ordering
::SeqCst
);
177 /// Print the symbol of the backtrace frame.
179 /// These output functions should now be used everywhere to ensure consistency.
180 /// You may want to also use `output_fileline`.
181 fn output(w
: &mut Write
, idx
: usize, frame
: Frame
,
182 s
: Option
<&str>, format
: PrintFormat
) -> io
::Result
<()> {
183 // Remove the `17: 0x0 - <unknown>` line.
184 if format
== PrintFormat
::Short
&& frame
.exact_position
== ptr
::null() {
188 PrintFormat
::Full
=> write
!(w
,
191 frame
.exact_position
,
193 PrintFormat
::Short
=> write
!(w
, " {:2}: ", idx
)?
,
196 Some(string
) => demangle(w
, string
, format
)?
,
197 None
=> w
.write_all(b
"<unknown>")?
,
202 /// Print the filename and line number of the backtrace frame.
204 /// See also `output`.
206 fn output_fileline(w
: &mut Write
, file
: &[u8], line
: libc
::c_int
,
207 format
: PrintFormat
) -> io
::Result
<()> {
208 // prior line: " ##: {:2$} - func"
211 PrintFormat
::Full
=> write
!(w
,
215 PrintFormat
::Short
=> write
!(w
, " ")?
,
218 let file
= str::from_utf8(file
).unwrap_or("<unknown>");
219 let file_path
= Path
::new(file
);
220 let mut already_printed
= false;
221 if format
== PrintFormat
::Short
&& file_path
.is_absolute() {
222 if let Ok(cwd
) = env
::current_dir() {
223 if let Ok(stripped
) = file_path
.strip_prefix(&cwd
) {
224 if let Some(s
) = stripped
.to_str() {
225 write
!(w
, " at .{}{}:{}", path
::MAIN_SEPARATOR
, s
, line
)?
;
226 already_printed
= true;
231 if !already_printed
{
232 write
!(w
, " at {}:{}", file
, line
)?
;
239 // All rust symbols are in theory lists of "::"-separated identifiers. Some
240 // assemblers, however, can't handle these characters in symbol names. To get
241 // around this, we use C++-style mangling. The mangling method is:
243 // 1. Prefix the symbol with "_ZN"
244 // 2. For each element of the path, emit the length plus the element
245 // 3. End the path with "E"
247 // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
249 // We're the ones printing our backtraces, so we can't rely on anything else to
250 // demangle our symbols. It's *much* nicer to look at demangled symbols, so
251 // this function is implemented to give us nice pretty output.
253 // Note that this demangler isn't quite as fancy as it could be. We have lots
254 // of other information in our symbols like hashes, version, type information,
255 // etc. Additionally, this doesn't handle glue symbols at all.
256 pub fn demangle(writer
: &mut Write
, s
: &str, format
: PrintFormat
) -> io
::Result
<()> {
257 // First validate the symbol. If it doesn't look like anything we're
258 // expecting, we just print it literally. Note that we must handle non-rust
259 // symbols because we could have any function in the backtrace.
260 let mut valid
= true;
262 if s
.len() > 4 && s
.starts_with("_ZN") && s
.ends_with("E") {
263 inner
= &s
[3 .. s
.len() - 1];
264 // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
265 } else if s
.len() > 3 && s
.starts_with("ZN") && s
.ends_with("E") {
266 inner
= &s
[2 .. s
.len() - 1];
272 let mut chars
= inner
.chars();
275 for c
in chars
.by_ref() {
277 i
= i
* 10 + c
as usize - '
0'
as usize;
283 valid
= chars
.next().is_none();
285 } else if chars
.by_ref().take(i
- 1).count() != i
- 1 {
291 // Alright, let's do this.
293 writer
.write_all(s
.as_bytes())?
;
295 // remove the `::hfc2edb670e5eda97` part at the end of the symbol.
296 if format
== PrintFormat
::Short
{
297 // The symbol in still mangled.
298 let mut split
= inner
.rsplitn(2, "17h");
299 match (split
.next(), split
.next()) {
300 (Some(addr
), rest
) => {
301 if addr
.len() == 16 &&
302 addr
.chars().all(|c
| c
.is_digit(16))
304 inner
= rest
.unwrap_or("");
311 let mut first
= true;
312 while !inner
.is_empty() {
314 writer
.write_all(b
"::")?
;
318 let mut rest
= inner
;
319 while rest
.chars().next().unwrap().is_numeric() {
322 let i
: usize = inner
[.. (inner
.len() - rest
.len())].parse().unwrap();
325 if rest
.starts_with("_$") {
328 while !rest
.is_empty() {
329 if rest
.starts_with(".") {
330 if let Some('
.'
) = rest
[1..].chars().next() {
331 writer
.write_all(b
"::")?
;
334 writer
.write_all(b
".")?
;
337 } else if rest
.starts_with("$") {
338 macro_rules
! demangle
{
339 ($
($pat
:expr
=> $demangled
:expr
),*) => ({
340 $
(if rest
.starts_with($pat
) {
341 writer
.write_all($demangled
)?
;
342 rest
= &rest
[$pat
.len()..];
345 writer
.write_all(rest
.as_bytes())?
;
352 // see src/librustc/back/link.rs for these mappings
363 // in theory we can demangle any Unicode code point, but
364 // for simplicity we just catch the common ones.
377 let idx
= match rest
.char_indices().find(|&(_
, c
)| c
== '$'
|| c
== '
.'
) {
381 writer
.write_all(rest
[..idx
].as_bytes())?
;
394 macro_rules
! t
{ ($a
:expr
, $b
:expr
) => ({
395 let mut m
= Vec
::new();
396 sys_common
::backtrace
::demangle(&mut m
,
398 super::PrintFormat
::Full
).unwrap();
399 assert_eq
!(String
::from_utf8(m
).unwrap(), $b
);
405 t
!("_ZN4testE", "test");
406 t
!("_ZN4test", "_ZN4test");
407 t
!("_ZN4test1a2bcE", "test::a::bc");
411 fn demangle_dollars() {
412 t
!("_ZN4$RP$E", ")");
413 t
!("_ZN8$RF$testE", "&test");
414 t
!("_ZN8$BP$test4foobE", "*test::foob");
415 t
!("_ZN9$u20$test4foobE", " test::foob");
416 t
!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
420 fn demangle_many_dollars() {
421 t
!("_ZN13test$u20$test4foobE", "test test::foob");
422 t
!("_ZN12test$BP$test4foobE", "test*test::foob");
426 fn demangle_windows() {
427 t
!("ZN4testE", "test");
428 t
!("ZN13test$u20$test4foobE", "test test::foob");
429 t
!("ZN12test$RF$test4foobE", "test&test::foob");
433 fn demangle_elements_beginning_with_underscore() {
434 t
!("_ZN13_$LT$test$GT$E", "<test>");
435 t
!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
436 t
!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
440 fn demangle_trait_impls() {
441 t
!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
442 "<Test + 'static as foo::Bar<Test>>::bar");