rust/src/libstd/sys_common/backtrace.rs

258 lines
8.7 KiB
Rust
Raw Normal View History

// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![cfg_attr(target_os = "nacl", allow(dead_code))]
use env;
use io::prelude::*;
use io;
use libc;
use str;
use sync::atomic::{self, Ordering};
pub use sys::backtrace::write;
2015-01-16 17:01:02 +02:00
#[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18;
2015-01-16 17:01:02 +02:00
#[cfg(target_pointer_width = "32")]
pub const HEX_WIDTH: usize = 10;
// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
match ENABLED.load(Ordering::SeqCst) {
1 => return false,
2 => return true,
_ => {}
}
let val = match env::var_os("RUST_BACKTRACE") {
allow RUST_BACKTRACE=0 to act as if unset /# This is a combination of 16 commits. /# The first commit's message is: allow RUST_BACKTRACE=disabled to act as if unset When RUST_BACKTRACE is set to "disabled" then this acts as if the env. var is unset. /# This is the 2nd commit message: case insensitive "DiSaBLeD" RUST_BACKTRACE value previously it expected a lowercase "disabled" to treat the env. var as unset /# This is the 3rd commit message: RUST_BACKTRACE=0 acts as if unset previously RUST_BACKTRACE=disabled was doing the same thing /# This is the 4th commit message: RUST_BACKTRACE=0|n|no|off acts as if unset previously only RUST_BACKTRACE=0 acted as if RUST_BACKTRACE was unset Now added more options (case-insensitive): 'n','no' and 'off' eg. RUST_BACKTRACE=oFF /# This is the 5th commit message: DRY on the value of 2 DRY=don't repeat yourself Because having to remember to keep the two places of '2' in sync is not ideal, even though this is a simple enough case. /# This is the 6th commit message: Revert "DRY on the value of 2" This reverts commit 95a0479d5cf72a2b2d9d21ec0bed2823ed213fef. Nevermind this DRY on 2, because we already have a RY on 1, besides the code is less readable this way... /# This is the 7th commit message: attempt to document unsetting RUST_BACKTRACE /# This is the 8th commit message: curb allocations when checking for RUST_BACKTRACE this means we don't check for case-insensitivity anymore /# This is the 9th commit message: as decided, RUST_BACKTRACE=0 turns off backtrace /# This is the 10th commit message: RUST_TEST_NOCAPTURE=0 acts as if unset (that is, capture is on) Any other value acts as if nocapture is enabled (that is, capture is off) /# This is the 11th commit message: update other RUST_TEST_NOCAPTURE occurrences apparently only one place needs updating /# This is the 12th commit message: update RUST_BACKTRACE in man page /# This is the 13th commit message: handle an occurrence of RUST_BACKTRACE /# This is the 14th commit message: ensure consistency with new rules for backtrace /# This is the 15th commit message: a more concise comment for RUST_TEST_NOCAPTURE /# This is the 16th commit message: update RUST_TEST_NOCAPTURE in man page
2016-03-28 14:41:55 +02:00
Some(x) => if &x == "0" { 1 } else { 2 },
None => 1,
};
ENABLED.store(val, Ordering::SeqCst);
val == 2
}
// These output functions should now be used everywhere to ensure consistency.
pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
s: Option<&[u8]>) -> io::Result<()> {
write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
match s.and_then(|s| str::from_utf8(s).ok()) {
Some(string) => demangle(w, string)?,
None => write!(w, "<unknown>")?,
}
w.write_all(&['\n' as u8])
}
#[allow(dead_code)]
pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
more: bool) -> io::Result<()> {
let file = str::from_utf8(file).unwrap_or("<unknown>");
// prior line: " ##: {:2$} - func"
write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
if more {
write!(w, " <... and possibly more>")?;
}
w.write_all(&['\n' as u8])
}
// All rust symbols are in theory lists of "::"-separated identifiers. Some
// assemblers, however, can't handle these characters in symbol names. To get
// around this, we use C++-style mangling. The mangling method is:
//
// 1. Prefix the symbol with "_ZN"
// 2. For each element of the path, emit the length plus the element
// 3. End the path with "E"
//
// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
//
// We're the ones printing our backtraces, so we can't rely on anything else to
// demangle our symbols. It's *much* nicer to look at demangled symbols, so
// this function is implemented to give us nice pretty output.
//
// Note that this demangler isn't quite as fancy as it could be. We have lots
// of other information in our symbols like hashes, version, type information,
// etc. Additionally, this doesn't handle glue symbols at all.
2015-03-11 15:24:14 -07:00
pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
// First validate the symbol. If it doesn't look like anything we're
// expecting, we just print it literally. Note that we must handle non-rust
// symbols because we could have any function in the backtrace.
let mut valid = true;
2014-12-18 19:41:20 -08:00
let mut inner = s;
if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
2015-01-17 16:15:52 -08:00
inner = &s[3 .. s.len() - 1];
2014-12-18 19:41:20 -08:00
// On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
} else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") {
2015-01-17 16:15:52 -08:00
inner = &s[2 .. s.len() - 1];
2014-12-18 19:41:20 -08:00
} else {
valid = false;
}
if valid {
let mut chars = inner.chars();
while valid {
let mut i = 0;
2015-01-23 13:16:03 -05:00
for c in chars.by_ref() {
if c.is_numeric() {
i = i * 10 + c as usize - '0' as usize;
} else {
break
}
}
if i == 0 {
valid = chars.next().is_none();
break
} else if chars.by_ref().take(i - 1).count() != i - 1 {
valid = false;
}
}
}
// Alright, let's do this.
if !valid {
writer.write_all(s.as_bytes())?;
} else {
let mut first = true;
while !inner.is_empty() {
if !first {
writer.write_all(b"::")?;
} else {
first = false;
}
2014-12-18 19:41:20 -08:00
let mut rest = inner;
std: Stabilize APIs for the 1.9 release This commit applies all stabilizations, renamings, and deprecations that the library team has decided on for the upcoming 1.9 release. All tracking issues have gone through a cycle-long "final comment period" and the specific APIs stabilized/deprecated are: Stable * `std::panic` * `std::panic::catch_unwind` (renamed from `recover`) * `std::panic::resume_unwind` (renamed from `propagate`) * `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`) * `std::panic::UnwindSafe` (renamed from `RecoverSafe`) * `str::is_char_boundary` * `<*const T>::as_ref` * `<*mut T>::as_ref` * `<*mut T>::as_mut` * `AsciiExt::make_ascii_uppercase` * `AsciiExt::make_ascii_lowercase` * `char::decode_utf16` * `char::DecodeUtf16` * `char::DecodeUtf16Error` * `char::DecodeUtf16Error::unpaired_surrogate` * `BTreeSet::take` * `BTreeSet::replace` * `BTreeSet::get` * `HashSet::take` * `HashSet::replace` * `HashSet::get` * `OsString::with_capacity` * `OsString::clear` * `OsString::capacity` * `OsString::reserve` * `OsString::reserve_exact` * `OsStr::is_empty` * `OsStr::len` * `std::os::unix::thread` * `RawPthread` * `JoinHandleExt` * `JoinHandleExt::as_pthread_t` * `JoinHandleExt::into_pthread_t` * `HashSet::hasher` * `HashMap::hasher` * `CommandExt::exec` * `File::try_clone` * `SocketAddr::set_ip` * `SocketAddr::set_port` * `SocketAddrV4::set_ip` * `SocketAddrV4::set_port` * `SocketAddrV6::set_ip` * `SocketAddrV6::set_port` * `SocketAddrV6::set_flowinfo` * `SocketAddrV6::set_scope_id` * `<[T]>::copy_from_slice` * `ptr::read_volatile` * `ptr::write_volatile` * The `#[deprecated]` attribute * `OpenOptions::create_new` Deprecated * `std::raw::Slice` - use raw parts of `slice` module instead * `std::raw::Repr` - use raw parts of `slice` module instead * `str::char_range_at` - use slicing plus `chars()` plus `len_utf8` * `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8` * `str::char_at` - use slicing plus `chars()` * `str::char_at_reverse` - use slicing plus `chars().rev()` * `str::slice_shift_char` - use `chars()` plus `Chars::as_str` * `CommandExt::session_leader` - use `before_exec` instead. Closes #27719 cc #27751 (deprecating the `Slice` bits) Closes #27754 Closes #27780 Closes #27809 Closes #27811 Closes #27830 Closes #28050 Closes #29453 Closes #29791 Closes #29935 Closes #30014 Closes #30752 Closes #31262 cc #31398 (still need to deal with `before_exec`) Closes #31405 Closes #31572 Closes #31755 Closes #31756
2016-04-07 10:42:53 -07:00
while rest.chars().next().unwrap().is_numeric() {
2015-01-17 16:15:52 -08:00
rest = &rest[1..];
}
let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
2015-01-17 16:15:52 -08:00
inner = &rest[i..];
rest = &rest[..i];
if rest.starts_with("_$") {
rest = &rest[1..];
}
while !rest.is_empty() {
if rest.starts_with(".") {
if let Some('.') = rest[1..].chars().next() {
writer.write_all(b"::")?;
rest = &rest[2..];
} else {
writer.write_all(b".")?;
rest = &rest[1..];
}
} else if rest.starts_with("$") {
2014-12-18 19:41:20 -08:00
macro_rules! demangle {
($($pat:expr => $demangled:expr),*) => ({
$(if rest.starts_with($pat) {
2016-08-27 07:33:36 -07:00
writer.write_all($demangled)?;
rest = &rest[$pat.len()..];
} else)*
{
2016-08-27 07:33:36 -07:00
writer.write_all(rest.as_bytes())?;
break;
}
})
2014-12-18 19:41:20 -08:00
}
// see src/librustc/back/link.rs for these mappings
demangle! (
"$SP$" => b"@",
"$BP$" => b"*",
"$RF$" => b"&",
"$LT$" => b"<",
"$GT$" => b">",
"$LP$" => b"(",
"$RP$" => b")",
"$C$" => b",",
// in theory we can demangle any Unicode code point, but
// for simplicity we just catch the common ones.
"$u7e$" => b"~",
"$u20$" => b" ",
"$u27$" => b"'",
"$u5b$" => b"[",
"$u5d$" => b"]",
"$u7b$" => b"{",
"$u7d$" => b"}",
"$u3b$" => b";",
"$u2b$" => b"+",
"$u22$" => b"\""
)
} else {
let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') {
None => rest.len(),
Some((i, _)) => i,
};
writer.write_all(rest[..idx].as_bytes())?;
2015-01-17 16:15:52 -08:00
rest = &rest[idx..];
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use sys_common;
macro_rules! t { ($a:expr, $b:expr) => ({
let mut m = Vec::new();
sys_common::backtrace::demangle(&mut m, $a).unwrap();
assert_eq!(String::from_utf8(m).unwrap(), $b);
}) }
#[test]
fn demangle() {
t!("test", "test");
t!("_ZN4testE", "test");
t!("_ZN4test", "_ZN4test");
t!("_ZN4test1a2bcE", "test::a::bc");
}
#[test]
fn demangle_dollars() {
t!("_ZN4$RP$E", ")");
t!("_ZN8$RF$testE", "&test");
t!("_ZN8$BP$test4foobE", "*test::foob");
t!("_ZN9$u20$test4foobE", " test::foob");
t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
}
#[test]
fn demangle_many_dollars() {
t!("_ZN13test$u20$test4foobE", "test test::foob");
t!("_ZN12test$BP$test4foobE", "test*test::foob");
}
#[test]
fn demangle_windows() {
t!("ZN4testE", "test");
t!("ZN13test$u20$test4foobE", "test test::foob");
t!("ZN12test$RF$test4foobE", "test&test::foob");
}
#[test]
fn demangle_elements_beginning_with_underscore() {
t!("_ZN13_$LT$test$GT$E", "<test>");
t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
}
#[test]
fn demangle_trait_impls() {
t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
"<Test + 'static as foo::Bar<Test>>::bar");
}
}