rust/src/libstd/sys/common/backtrace.rs
Alex Crichton 552eda70d3 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-11 08:57:53 -07:00

229 lines
7.5 KiB
Rust

// 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;
#[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18;
#[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") {
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.
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;
let mut inner = s;
if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
inner = &s[3 .. s.len() - 1];
// 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") {
inner = &s[2 .. s.len() - 1];
} else {
valid = false;
}
if valid {
let mut chars = inner.chars();
while valid {
let mut i = 0;
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;
}
let mut rest = inner;
while rest.chars().next().unwrap().is_numeric() {
rest = &rest[1..];
}
let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
inner = &rest[i..];
rest = &rest[..i];
while !rest.is_empty() {
if rest.starts_with("$") {
macro_rules! demangle {
($($pat:expr, => $demangled:expr),*) => ({
$(if rest.starts_with($pat) {
try!(writer.write_all($demangled));
rest = &rest[$pat.len()..];
} else)*
{
try!(writer.write_all(rest.as_bytes()));
break;
}
})
}
// 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"]"
)
} else {
let idx = match rest.find('$') {
None => rest.len(),
Some(i) => i,
};
writer.write_all(rest[..idx].as_bytes())?;
rest = &rest[idx..];
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use prelude::v1::*;
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");
}
#[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");
}
}