diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 99724e29e02b2..002b1575c8bfb 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -159,7 +159,10 @@ enum RawFrame { Fake, } -struct BacktraceSymbol { +/// A single symbol associated with a backtrace frame, which may include a +/// function name, filename, line number, and column number. +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] +pub struct BacktraceSymbol { name: Option>, filename: Option, lineno: Option, @@ -207,6 +210,41 @@ impl fmt::Debug for BacktraceFrame { } } +///NOTE: This is never intended to become stable. It doesn't have an ACP +/// Its meant to help us port the tests mentioned in issue #122899 +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] +impl BacktraceFrame { + /// Returns the instruction pointer associated with this frame. + pub fn ip(&self) -> *mut c_void { + self.frame.ip() + } + + /// Returns the symbols associated with this frame, if any. + pub fn symbols(&self) -> &[BacktraceSymbol] { + &self.symbols + } + + /// Returns the base address of the module this frame belongs to, if available. + pub fn module_base_address(&self) -> Option<*mut c_void> { + match &self.frame { + RawFrame::Actual(frame) => frame.module_base_address(), + #[cfg(test)] + RawFrame::Fake => None, + } + } +} + +///NOTE: This is never intended to become stable. It doesn't have an ACP +/// Its meant to help us port the tests mentioned in issue #122899 +impl BacktraceSymbol { + /// Returns the name of this symbol, if available. + #[unstable(feature = "backtrace_internals_accessors", issue = "122899")] + pub fn name(&self) -> Option<&[u8]> { + self.name.as_deref() + } +} + +#[unstable(feature = "backtrace_internals_accessors", issue = "122899")] impl fmt::Debug for BacktraceSymbol { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME: improve formatting: https://github.com/rust-lang/rust/issues/65280 diff --git a/tests/ui/README.md b/tests/ui/README.md index 16cdde08431c0..6e4d707392a00 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -973,6 +973,10 @@ See [RFC 3550 New Range](https://github.com/rust-lang/rfcs/blob/master/text/3550 Tests for Non-lexical lifetimes. See [RFC 2094 NLL](https://rust-lang.github.io/rfcs/2094-nll.html). +## `tests/ui/no_debuginfo`: without_debuginfo test ported from backtrace-rs + +Tests for behavior when debug information is not available, ported from `backtrace-rs`'s `without_debuginfo` crate's test. + ## `tests/ui/no_std/` Tests for where the standard library is disabled through `#![no_std]`. diff --git a/tests/ui/no_debuginfo/all_frames_have_module_base_address.rs b/tests/ui/no_debuginfo/all_frames_have_module_base_address.rs new file mode 100644 index 0000000000000..35e66023a5b63 --- /dev/null +++ b/tests/ui/no_debuginfo/all_frames_have_module_base_address.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Cstrip=none -Cdebuginfo=none +//@ run-pass +#![feature(backtrace_frames)] +#![feature(backtrace_internals_accessors)] +use std::backtrace; + +fn main() { + let mut missing_base_addresses = 0; + let btrace = backtrace::Backtrace::force_capture(); + let frames = btrace.frames(); + for frame in frames { + if frame.module_base_address().is_none() { + missing_base_addresses += 1; + } + } + + if cfg!(windows) { + assert_eq!(missing_base_addresses, 0); + } +} diff --git a/tests/ui/no_debuginfo/all_frames_have_symbols.rs b/tests/ui/no_debuginfo/all_frames_have_symbols.rs new file mode 100644 index 0000000000000..a2001c98162b2 --- /dev/null +++ b/tests/ui/no_debuginfo/all_frames_have_symbols.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Cstrip=none -Cdebuginfo=none +//@ run-pass +#![feature(backtrace_frames)] +#![feature(backtrace_internals_accessors)] +use std::backtrace; +fn main() { + let mut missing_symbols = 0; + let mut has_symbols = 0; + let btrace = backtrace::Backtrace::force_capture(); + let frames = btrace.frames(); + let mut missing_symbol_indices = Vec::new(); + for (i, frame) in frames.iter().enumerate() { + let mut any = false; + for sym in frame.symbols() { + if sym.name().is_some() { + any = true; + break; + } + } + if any { + has_symbols += 1; + } else if !frame.ip().is_null() { + missing_symbols += 1; + missing_symbol_indices.push(i); + } + } + + // FIXME(#346) currently on MinGW we can't symbolize kernel32.dll and other + // system libraries, which means we miss the last few symbols. + if cfg!(windows) && cfg!(target_env = "gnu") { + assert!(missing_symbols < has_symbols && has_symbols > 4); + } else if cfg!(all(target_os = "linux", target_env = "gnu")) { + //NOTE: The reason we allow one missing symbol is because the frame for the + // `__libc_start_main` fn doesn't have a symbol. See the discussion in + // #152860 for more details. + assert!(missing_symbols < has_symbols && missing_symbols <= 1) + } else { + for i in missing_symbol_indices { + eprintln!("missing symbol for frame {i}: {:#?}", frames[i]); + } + eprintln!("Full erroneous backtrace: {:#?}", btrace); + assert_eq!(missing_symbols, 0); + } +}