diff options
| author | phosphorus <steepout@qq.com> | 2019-08-19 00:34:02 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-19 00:34:02 -0500 |
| commit | 92f08b78a12ff119af853cb2bf58468208ea6a90 (patch) | |
| tree | b4636f43c056de11dd69130ce47039343a9f52c5 /src/libstd/sys_common | |
| parent | 963184bbb670c1ffa97fc28a98cd5e8473118859 (diff) | |
| parent | a807902dd6b4222179776c3f3c33da8dafdd4da1 (diff) | |
| download | rust-92f08b78a12ff119af853cb2bf58468208ea6a90.tar.gz rust-92f08b78a12ff119af853cb2bf58468208ea6a90.zip | |
Merge pull request #1 from rust-lang/master
Pull from newest repo
Diffstat (limited to 'src/libstd/sys_common')
| -rw-r--r-- | src/libstd/sys_common/alloc.rs | 3 | ||||
| -rw-r--r-- | src/libstd/sys_common/backtrace.rs | 304 | ||||
| -rw-r--r-- | src/libstd/sys_common/gnu/libbacktrace.rs | 175 | ||||
| -rw-r--r-- | src/libstd/sys_common/gnu/mod.rs | 5 | ||||
| -rw-r--r-- | src/libstd/sys_common/io.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys_common/mod.rs | 9 | ||||
| -rw-r--r-- | src/libstd/sys_common/os_str_bytes.rs | 6 |
7 files changed, 163 insertions, 341 deletions
diff --git a/src/libstd/sys_common/alloc.rs b/src/libstd/sys_common/alloc.rs index 978a70bee09..1cfc7ed17f2 100644 --- a/src/libstd/sys_common/alloc.rs +++ b/src/libstd/sys_common/alloc.rs @@ -12,7 +12,8 @@ use crate::ptr; target_arch = "powerpc", target_arch = "powerpc64", target_arch = "asmjs", - target_arch = "wasm32")))] + target_arch = "wasm32", + target_arch = "hexagon")))] pub const MIN_ALIGN: usize = 8; #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64", diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 8d8d8169b43..bf37ff7ddbd 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -2,40 +2,17 @@ /// supported platforms. use crate::env; -use crate::io::prelude::*; use crate::io; +use crate::io::prelude::*; +use crate::mem; use crate::path::{self, Path}; use crate::ptr; -use crate::str; use crate::sync::atomic::{self, Ordering}; use crate::sys::mutex::Mutex; -use rustc_demangle::demangle; - -pub use crate::sys::backtrace::{ - unwind_backtrace, - resolve_symname, - foreach_symbol_fileline, - BacktraceContext -}; - -#[cfg(target_pointer_width = "64")] -pub const HEX_WIDTH: usize = 18; - -#[cfg(target_pointer_width = "32")] -pub const HEX_WIDTH: usize = 10; - -/// Represents an item in the backtrace list. See `unwind_backtrace` for how -/// it is created. -#[derive(Debug, Copy, Clone)] -pub struct Frame { - /// Exact address of the call that failed. - pub exact_position: *const u8, - /// Address of the enclosing function. - pub symbol_addr: *const u8, - /// Which inlined function is this frame referring to - pub inline_context: u32, -} +use backtrace::{BytesOrWideString, Frame, Symbol}; + +pub const HEX_WIDTH: usize = 2 + 2 * mem::size_of::<usize>(); /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; @@ -49,7 +26,7 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { // test mode immediately return here to optimize away any references to the // libbacktrace symbols if cfg!(test) { - return Ok(()) + return Ok(()); } // Use a lock to prevent mixed output in multithreading context. @@ -63,75 +40,39 @@ pub fn print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { } fn _print(w: &mut dyn Write, format: PrintFormat) -> io::Result<()> { - let mut frames = [Frame { - exact_position: ptr::null(), - symbol_addr: ptr::null(), - inline_context: 0, - }; MAX_NB_FRAMES]; - let (nb_frames, context) = unwind_backtrace(&mut frames)?; - let (skipped_before, skipped_after) = - filter_frames(&frames[..nb_frames], format, &context); - if skipped_before + skipped_after > 0 { - writeln!(w, "note: Some details are omitted, \ - run with `RUST_BACKTRACE=full` for a verbose backtrace.")?; - } writeln!(w, "stack backtrace:")?; - let filtered_frames = &frames[..nb_frames - skipped_after]; - for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() { - resolve_symname(*frame, |symname| { - output(w, index, *frame, symname, format) - }, &context)?; - let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| { - output_fileline(w, file, line, format) - }, &context)?; - if has_more_filenames { - w.write_all(b" <... and possibly more>")?; - } - } - - Ok(()) -} - -/// Returns a number of frames to remove at the beginning and at the end of the -/// backtrace, according to the backtrace format. -fn filter_frames(frames: &[Frame], - format: PrintFormat, - context: &BacktraceContext) -> (usize, usize) -{ - if format == PrintFormat::Full { - return (0, 0); - } - - let skipped_before = 0; - - let skipped_after = frames.len() - frames.iter().position(|frame| { - let mut is_marker = false; - let _ = resolve_symname(*frame, |symname| { - if let Some(mangled_symbol_name) = symname { - // Use grep to find the concerned functions - if mangled_symbol_name.contains("__rust_begin_short_backtrace") { - is_marker = true; - } + let mut printer = Printer::new(format, w); + unsafe { + backtrace::trace_unsynchronized(|frame| { + let mut hit = false; + backtrace::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + printer.output(frame, Some(symbol)); + }); + if !hit { + printer.output(frame, None); } - Ok(()) - }, context); - is_marker - }).unwrap_or(frames.len()); - - if skipped_before + skipped_after >= frames.len() { - // Avoid showing completely empty backtraces - return (0, 0); + !printer.done + }); } - - (skipped_before, skipped_after) + if printer.skipped { + writeln!( + w, + "note: Some details are omitted, \ + run with `RUST_BACKTRACE=full` for a verbose backtrace." + )?; + } + Ok(()) } - /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. #[inline(never)] pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T - where F: FnOnce() -> T, F: Send, T: Send +where + F: FnOnce() -> T, + F: Send, + T: Send, { f() } @@ -156,7 +97,7 @@ pub fn log_enabled() -> Option<PrintFormat> { _ => return Some(PrintFormat::Full), } - let val = env::var_os("RUST_BACKTRACE").and_then(|x| + let val = env::var_os("RUST_BACKTRACE").and_then(|x| { if &x == "0" { None } else if &x == "full" { @@ -164,80 +105,141 @@ pub fn log_enabled() -> Option<PrintFormat> { } else { Some(PrintFormat::Short) } + }); + ENABLED.store( + match val { + Some(v) => v as isize, + None => 1, + }, + Ordering::SeqCst, ); - ENABLED.store(match val { - Some(v) => v as isize, - None => 1, - }, Ordering::SeqCst); val } -/// Prints the symbol of the backtrace frame. -/// -/// These output functions should now be used everywhere to ensure consistency. -/// You may want to also use `output_fileline`. -fn output(w: &mut dyn Write, idx: usize, frame: Frame, - s: Option<&str>, format: PrintFormat) -> io::Result<()> { - // Remove the `17: 0x0 - <unknown>` line. - if format == PrintFormat::Short && frame.exact_position == ptr::null() { - return Ok(()); +struct Printer<'a, 'b> { + format: PrintFormat, + done: bool, + skipped: bool, + idx: usize, + out: &'a mut (dyn Write + 'b), +} + +impl<'a, 'b> Printer<'a, 'b> { + fn new(format: PrintFormat, out: &'a mut (dyn Write + 'b)) -> Printer<'a, 'b> { + Printer { format, done: false, skipped: false, idx: 0, out } } - match format { - PrintFormat::Full => write!(w, - " {:2}: {:2$?} - ", - idx, - frame.exact_position, - HEX_WIDTH)?, - PrintFormat::Short => write!(w, " {:2}: ", idx)?, + + /// Prints the symbol of the backtrace frame. + /// + /// These output functions should now be used everywhere to ensure consistency. + /// You may want to also use `output_fileline`. + fn output(&mut self, frame: &Frame, symbol: Option<&Symbol>) { + if self.idx > MAX_NB_FRAMES { + self.done = true; + self.skipped = true; + return; + } + if self._output(frame, symbol).is_err() { + self.done = true; + } + self.idx += 1; } - match s { - Some(string) => { - let symbol = demangle(string); - match format { - PrintFormat::Full => write!(w, "{}", symbol)?, - // strip the trailing hash if short mode - PrintFormat::Short => write!(w, "{:#}", symbol)?, + + fn _output(&mut self, frame: &Frame, symbol: Option<&Symbol>) -> io::Result<()> { + if self.format == PrintFormat::Short { + if let Some(sym) = symbol.and_then(|s| s.name()).and_then(|s| s.as_str()) { + if sym.contains("__rust_begin_short_backtrace") { + self.skipped = true; + self.done = true; + return Ok(()); + } + } + + // Remove the `17: 0x0 - <unknown>` line. + if self.format == PrintFormat::Short && frame.ip() == ptr::null_mut() { + self.skipped = true; + return Ok(()); } } - None => w.write_all(b"<unknown>")?, - } - w.write_all(b"\n") -} -/// Prints the filename and line number of the backtrace frame. -/// -/// See also `output`. -#[allow(dead_code)] -fn output_fileline(w: &mut dyn Write, - file: &[u8], - line: u32, - format: PrintFormat) -> io::Result<()> { - // prior line: " ##: {:2$} - func" - w.write_all(b"")?; - match format { - PrintFormat::Full => write!(w, - " {:1$}", - "", - HEX_WIDTH)?, - PrintFormat::Short => write!(w, " ")?, - } + match self.format { + PrintFormat::Full => { + write!(self.out, " {:2}: {:2$?} - ", self.idx, frame.ip(), HEX_WIDTH)? + } + PrintFormat::Short => write!(self.out, " {:2}: ", self.idx)?, + } - let file = str::from_utf8(file).unwrap_or("<unknown>"); - let file_path = Path::new(file); - let mut already_printed = false; - if format == PrintFormat::Short && file_path.is_absolute() { - if let Ok(cwd) = env::current_dir() { - if let Ok(stripped) = file_path.strip_prefix(&cwd) { - if let Some(s) = stripped.to_str() { - write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?; - already_printed = true; + match symbol.and_then(|s| s.name()) { + Some(symbol) => { + match self.format { + PrintFormat::Full => write!(self.out, "{}", symbol)?, + // Strip the trailing hash if short mode. + PrintFormat::Short => write!(self.out, "{:#}", symbol)?, } } + None => self.out.write_all(b"<unknown>")?, } - } - if !already_printed { - write!(w, " at {}:{}", file, line)?; + self.out.write_all(b"\n")?; + if let Some(sym) = symbol { + self.output_fileline(sym)?; + } + Ok(()) } - w.write_all(b"\n") + /// Prints the filename and line number of the backtrace frame. + /// + /// See also `output`. + fn output_fileline(&mut self, symbol: &Symbol) -> io::Result<()> { + #[cfg(windows)] + let path_buf; + let file = match symbol.filename_raw() { + #[cfg(unix)] + Some(BytesOrWideString::Bytes(bytes)) => { + use crate::os::unix::prelude::*; + Path::new(crate::ffi::OsStr::from_bytes(bytes)) + } + #[cfg(not(unix))] + Some(BytesOrWideString::Bytes(bytes)) => { + Path::new(crate::str::from_utf8(bytes).unwrap_or("<unknown>")) + } + #[cfg(windows)] + Some(BytesOrWideString::Wide(wide)) => { + use crate::os::windows::prelude::*; + path_buf = crate::ffi::OsString::from_wide(wide); + Path::new(&path_buf) + } + #[cfg(not(windows))] + Some(BytesOrWideString::Wide(_wide)) => { + Path::new("<unknown>") + } + None => return Ok(()), + }; + let line = match symbol.lineno() { + Some(line) => line, + None => return Ok(()), + }; + // prior line: " ##: {:2$} - func" + self.out.write_all(b"")?; + match self.format { + PrintFormat::Full => write!(self.out, " {:1$}", "", HEX_WIDTH)?, + PrintFormat::Short => write!(self.out, " ")?, + } + + let mut already_printed = false; + if self.format == PrintFormat::Short && file.is_absolute() { + if let Ok(cwd) = env::current_dir() { + if let Ok(stripped) = file.strip_prefix(&cwd) { + if let Some(s) = stripped.to_str() { + write!(self.out, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?; + already_printed = true; + } + } + } + } + if !already_printed { + write!(self.out, " at {}:{}", file.display(), line)?; + } + + self.out.write_all(b"\n") + } } diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs deleted file mode 100644 index 6cd050242dd..00000000000 --- a/src/libstd/sys_common/gnu/libbacktrace.rs +++ /dev/null @@ -1,175 +0,0 @@ -use backtrace_sys::backtrace_state; - -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::backtrace::BacktraceContext; -use crate::sys_common::backtrace::Frame; - -pub fn foreach_symbol_fileline<F>(frame: Frame, - mut f: F, - _: &BacktraceContext) -> io::Result<bool> -where F: FnMut(&[u8], u32) -> io::Result<()> -{ - // pcinfo may return an arbitrary number of file:line pairs, - // in the order of stack trace (i.e., inlined calls first). - // in order to avoid allocation, we stack-allocate a fixed size of entries. - const FILELINE_SIZE: usize = 32; - let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE]; - let ret; - let fileline_count = { - let state = unsafe { init_state() }; - if state.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to allocate libbacktrace state") - ) - } - let mut fileline_win: &mut [FileLine] = &mut fileline_buf; - let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; - ret = unsafe { - backtrace_sys::backtrace_pcinfo( - state, - frame.exact_position as libc::uintptr_t, - pcinfo_cb, - error_cb, - fileline_addr as *mut libc::c_void, - ) - }; - FILELINE_SIZE - fileline_win.len() - }; - if ret == 0 { - for &(file, line) in &fileline_buf[..fileline_count] { - if file.is_null() { continue; } // just to be sure - let file = unsafe { CStr::from_ptr(file).to_bytes() }; - f(file, line)?; - } - Ok(fileline_count == FILELINE_SIZE) - } else { - Ok(false) - } -} - -/// Converts a pointer to symbol to its string value. -pub fn resolve_symname<F>(frame: Frame, - callback: F, - _: &BacktraceContext) -> io::Result<()> - where F: FnOnce(Option<&str>) -> io::Result<()> -{ - let symname = { - let state = unsafe { init_state() }; - if state.is_null() { - return Err(io::Error::new( - io::ErrorKind::Other, - "failed to allocate libbacktrace state") - ) - } - let mut data: *const libc::c_char = ptr::null(); - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_sys::backtrace_syminfo( - state, - frame.symbol_addr as libc::uintptr_t, - syminfo_cb, - error_cb, - data_addr as *mut libc::c_void, - ) - }; - if ret == 0 || data.is_null() { - None - } else { - unsafe { - CStr::from_ptr(data).to_str().ok() - } - } - }; - callback(symname) -} - -//////////////////////////////////////////////////////////////////////// -// helper callbacks -//////////////////////////////////////////////////////////////////////// - -type FileLine = (*const libc::c_char, u32); - -extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now -} -extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } -} -extern fn pcinfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - filename: *const libc::c_char, - lineno: libc::c_int, - _function: *const libc::c_char) -> libc::c_int { - if !filename.is_null() { - let slot = data as *mut &mut [FileLine]; - let buffer = unsafe {ptr::read(slot)}; - - // if the buffer is not full, add file:line to the buffer - // and adjust the buffer for next possible calls to pcinfo_cb. - if !buffer.is_empty() { - buffer[0] = (filename, lineno as u32); - unsafe { ptr::write(slot, &mut buffer[1..]); } - } - } - - 0 -} - -// The libbacktrace API supports creating a state, but it does not -// support destroying a state. I personally take this to mean that a -// state is meant to be created and then live forever. -// -// I would love to register an at_exit() handler which cleans up this -// state, but libbacktrace provides no way to do so. -// -// With these constraints, this function has a statically cached state -// that is calculated the first time this is requested. Remember that -// backtracing all happens serially (one global lock). -// -// Things don't work so well on not-Linux since libbacktrace can't track -// down that executable this is. We at one point used env::current_exe but -// it turns out that there are some serious security issues with that -// approach. -// -// Specifically, on certain platforms like BSDs, a malicious actor can cause -// an arbitrary file to be placed at the path returned by current_exe. -// libbacktrace does not behave defensively in the presence of ill-formed -// DWARF information, and has been demonstrated to segfault in at least one -// case. There is no evidence at the moment to suggest that a more carefully -// constructed file can't cause arbitrary code execution. As a result of all -// of this, we don't hint libbacktrace with the path to the current process. -unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = ptr::null_mut(); - if !STATE.is_null() { return STATE } - - let filename = match crate::sys::backtrace::gnu::get_executable_filename() { - Ok((filename, file)) => { - // filename is purposely leaked here since libbacktrace requires - // it to stay allocated permanently, file is also leaked so that - // the file stays locked - let filename_ptr = filename.as_ptr(); - mem::forget(filename); - mem::forget(file); - filename_ptr - }, - Err(_) => ptr::null(), - }; - - STATE = backtrace_sys::backtrace_create_state( - filename, - 0, - error_cb, - ptr::null_mut(), - ); - STATE -} diff --git a/src/libstd/sys_common/gnu/mod.rs b/src/libstd/sys_common/gnu/mod.rs deleted file mode 100644 index d6959697f2a..00000000000 --- a/src/libstd/sys_common/gnu/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![allow(missing_docs)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -pub mod libbacktrace; diff --git a/src/libstd/sys_common/io.rs b/src/libstd/sys_common/io.rs index 44b0963302d..8789abe55c3 100644 --- a/src/libstd/sys_common/io.rs +++ b/src/libstd/sys_common/io.rs @@ -16,7 +16,7 @@ pub mod test { p.join(path) } - pub fn path<'a>(&'a self) -> &'a Path { + pub fn path(&self) -> &Path { let TempDir(ref p) = *self; p } diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 78e15994264..9190a3b0d5f 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -65,10 +65,9 @@ pub mod bytestring; pub mod process; pub mod fs; -cfg_if! { +cfg_if::cfg_if! { if #[cfg(any(target_os = "cloudabi", target_os = "l4re", - target_os = "redox", all(target_arch = "wasm32", not(target_os = "emscripten")), all(target_vendor = "fortanix", target_env = "sgx")))] { pub use crate::sys::net; @@ -77,12 +76,6 @@ cfg_if! { } } -#[cfg(feature = "backtrace")] -#[cfg(any(all(unix, not(target_os = "emscripten")), - all(windows, target_env = "gnu"), - target_os = "redox"))] -pub mod gnu; - // common error constructors /// A trait for viewing representations from std types diff --git a/src/libstd/sys_common/os_str_bytes.rs b/src/libstd/sys_common/os_str_bytes.rs index a4961974d89..d734f412bf8 100644 --- a/src/libstd/sys_common/os_str_bytes.rs +++ b/src/libstd/sys_common/os_str_bytes.rs @@ -18,6 +18,12 @@ pub(crate) struct Buf { pub inner: Vec<u8> } +// FIXME: +// `Buf::as_slice` current implementation relies +// on `Slice` being layout-compatible with `[u8]`. +// When attribute privacy is implemented, `Slice` should be annotated as `#[repr(transparent)]`. +// Anyway, `Slice` representation and layout are considered implementation detail, are +// not documented and must not be relied upon. pub(crate) struct Slice { pub inner: [u8] } |
