diff --git a/.github/workflows/check-binary-size.yml b/.github/workflows/check-binary-size.yml index 5f4ec6168..b53efa009 100644 --- a/.github/workflows/check-binary-size.yml +++ b/.github/workflows/check-binary-size.yml @@ -53,6 +53,7 @@ jobs: with: repository: rust-lang/rust path: ${{ env.RUSTC_DIR }} + ref: 'backtrace-debuginfo' - name: Set up std repository and backtrace submodule for size test shell: bash working-directory: ${{ env.RUSTC_DIR }} diff --git a/Cargo.lock b/Cargo.lock index 900db8974..3300eea08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,9 +82,8 @@ version = "0.1.0" [[package]] name = "gimli" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +version = "0.31.1" +source = "git+https://github.com/jyn514/gimli?branch=shared-attrs#afbceac5bbc683020ba696d23501f34ee6f67ea4" [[package]] name = "libc" diff --git a/Cargo.toml b/Cargo.toml index 8fa5f3fa9..f3eeac112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,9 @@ version = "0.36.0" default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'xcoff', 'unaligned', 'archive'] +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } + [dev-dependencies] dylib-dep = { path = "crates/dylib-dep" } libloading = "0.7" diff --git a/crates/as-if-std/Cargo.toml b/crates/as-if-std/Cargo.toml index a9c29cecf..e2e15d63d 100644 --- a/crates/as-if-std/Cargo.toml +++ b/crates/as-if-std/Cargo.toml @@ -36,3 +36,5 @@ std = [] [lints.rust] unexpected_cfgs = "allow" +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/cpp_smoke_test/Cargo.toml b/crates/cpp_smoke_test/Cargo.toml index e1e51d92f..8dd97c7d4 100644 --- a/crates/cpp_smoke_test/Cargo.toml +++ b/crates/cpp_smoke_test/Cargo.toml @@ -10,3 +10,5 @@ cc = "1.0" [dependencies] backtrace = { path = "../..", features = ["cpp_demangle"] } +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/debuglink/Cargo.toml b/crates/debuglink/Cargo.toml index 5e62abd37..c6d46a68b 100644 --- a/crates/debuglink/Cargo.toml +++ b/crates/debuglink/Cargo.toml @@ -5,3 +5,5 @@ edition = "2021" [dependencies] backtrace = { path = "../.." } +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/dylib-dep/Cargo.toml b/crates/dylib-dep/Cargo.toml index e6cc9c23b..af3e0a7d2 100644 --- a/crates/dylib-dep/Cargo.toml +++ b/crates/dylib-dep/Cargo.toml @@ -8,3 +8,5 @@ publish = false [lib] name = "dylib_dep" crate-type = ["cdylib", "rlib"] +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/line-tables-only/Cargo.toml b/crates/line-tables-only/Cargo.toml index 4aa28f6a0..f650d597f 100644 --- a/crates/line-tables-only/Cargo.toml +++ b/crates/line-tables-only/Cargo.toml @@ -15,3 +15,5 @@ features = [ 'libunwind', 'std', ] +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/macos_frames_test/Cargo.toml b/crates/macos_frames_test/Cargo.toml index 849e76414..287666df9 100644 --- a/crates/macos_frames_test/Cargo.toml +++ b/crates/macos_frames_test/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" [dependencies.backtrace] path = "../.." +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/crates/without_debuginfo/Cargo.toml b/crates/without_debuginfo/Cargo.toml index b8c939414..3871d7ae2 100644 --- a/crates/without_debuginfo/Cargo.toml +++ b/crates/without_debuginfo/Cargo.toml @@ -14,3 +14,5 @@ debug = false [profile.test] debug = false +[patch.crates-io] +gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" } diff --git a/src/lib.rs b/src/lib.rs index 0b9bb56ae..3fdb0c987 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,7 +111,7 @@ pub use self::backtrace::{trace_unsynchronized, Frame}; mod backtrace; pub use self::symbolize::resolve_frame_unsynchronized; -pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; +pub use self::symbolize::{resolve_unsynchronized, ShortBacktrace, Symbol, SymbolName}; mod symbolize; pub use self::types::BytesOrWideString; diff --git a/src/symbolize/dbghelp.rs b/src/symbolize/dbghelp.rs index d4c887ddd..fe46dfb9f 100644 --- a/src/symbolize/dbghelp.rs +++ b/src/symbolize/dbghelp.rs @@ -73,6 +73,12 @@ impl Symbol<'_> { self._filename_cache.as_ref().map(Path::new) } + + pub fn short_backtrace(&self) -> Option { + // Not supported with dllhelp API. + // FIXME: might be doable with [llvm.codeview.annotation](https://reviews.llvm.org/D36904)? + None + } } #[repr(C, align(8))] diff --git a/src/symbolize/gimli.rs b/src/symbolize/gimli.rs index 8c7051d4d..1d42729a0 100644 --- a/src/symbolize/gimli.rs +++ b/src/symbolize/gimli.rs @@ -8,8 +8,10 @@ use self::mmap::Mmap; use self::stash::Stash; use super::BytesOrWideString; use super::ResolveWhat; +use super::ShortBacktrace; use super::SymbolName; use addr2line::gimli; +use addr2line::{LookupContinuation, LookupResult}; use core::convert::TryInto; use core::mem; use core::u32; @@ -170,9 +172,24 @@ impl<'data> Context<'data> { stash: &'data Stash, probe: u64, ) -> gimli::Result>> { - use addr2line::{LookupContinuation, LookupResult}; + let continuation = self.dwarf.find_frames(probe); + self.continuation_helper(stash, continuation) + } + + fn find_dwarf_and_unit( + &'_ self, + stash: &'data Stash, + probe: u64, + ) -> Option>> { + let continuation = self.dwarf.find_dwarf_and_unit(probe); + self.continuation_helper(stash, continuation) + } - let mut l = self.dwarf.find_frames(probe); + fn continuation_helper( + &'_ self, + stash: &'data Stash, + mut l: LookupResult>>, + ) -> O { loop { let (load, continuation) = match l { LookupResult::Output(output) => break output, @@ -409,6 +426,43 @@ impl Cache { } } +impl ShortBacktrace { + fn from_raw(raw: u8) -> Option { + let this = match raw { + 0 => ShortBacktrace::ThisFrameOnly, + 1 => ShortBacktrace::Start, + 2 => ShortBacktrace::End, + _ => return None, + }; + Some(this) + } +} + +#[allow(non_upper_case_globals)] +const DW_AT_short_backtrace: gimli::DwAt = gimli::DwAt(0x3c00); + +fn parse_short_backtrace<'data, R: gimli::Reader>( + unit_ref: gimli::UnitRef<'_, R>, + frame: &addr2line::Frame<'_, R>, +) -> Option { + use core::ops::ControlFlow; + + let mut short_backtrace = None; + let _ = unit_ref.shared_attrs(frame.dw_die_offset?, 16, |attr, _| { + if attr.name() == DW_AT_short_backtrace { + let parsed = ShortBacktrace::from_raw( + attr.u8_value() + .ok_or(gimli::Error::UnsupportedAttributeForm)?, + ); + short_backtrace = Some(parsed.expect("rustc generated invalid debuginfo?")); + return Ok(ControlFlow::Break(())); + } + Ok(ControlFlow::Continue(())) + }); + + short_backtrace +} + pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { let addr = what.address_or_ip(); let mut call = |sym: Symbol<'_>| { @@ -435,24 +489,30 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) if let Ok(mut frames) = cx.find_frames(stash, addr as u64) { while let Ok(Some(frame)) = frames.next() { any_frames = true; - let name = match frame.function { + let name = match &frame.function { Some(f) => Some(f.name.slice()), None => cx.object.search_symtab(addr as u64), }; + let unit_ref = cx.find_dwarf_and_unit(stash, addr as u64); + let short_backtrace = unit_ref.and_then(|unit| parse_short_backtrace(unit, &frame)); call(Symbol::Frame { addr: addr as *mut c_void, location: frame.location, name, + short_backtrace, }); } } if !any_frames { if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) { + let unit_ref = None; if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) { while let Ok(Some(frame)) = frames.next() { any_frames = true; call(Symbol::Frame { addr: addr as *mut c_void, + short_backtrace: unit_ref + .and_then(|unit| parse_short_backtrace(unit, &frame)), location: frame.location, name: frame.function.map(|f| f.name.slice()), }); @@ -475,6 +535,7 @@ pub enum Symbol<'a> { addr: *mut c_void, location: Option>, name: Option<&'a [u8]>, + short_backtrace: Option, }, /// Couldn't find debug information, but we found it in the symbol table of /// the elf executable. @@ -532,4 +593,13 @@ impl Symbol<'_> { Symbol::Symtab { .. } => None, } } + + pub fn short_backtrace(&self) -> Option { + match self { + Symbol::Frame { + short_backtrace, .. + } => *short_backtrace, + Symbol::Symtab { .. } => None, + } + } } diff --git a/src/symbolize/gimli/coff.rs b/src/symbolize/gimli/coff.rs index 018cb494b..f25e2eabd 100644 --- a/src/symbolize/gimli/coff.rs +++ b/src/symbolize/gimli/coff.rs @@ -100,7 +100,7 @@ impl<'a> Object<'a> { self.symbols[i].1.name(self.strings).ok() } - pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> { None } } diff --git a/src/symbolize/gimli/elf.rs b/src/symbolize/gimli/elf.rs index 906a30054..2a5cf1824 100644 --- a/src/symbolize/gimli/elf.rs +++ b/src/symbolize/gimli/elf.rs @@ -238,7 +238,7 @@ impl<'a> Object<'a> { } } - pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> { None } diff --git a/src/symbolize/gimli/xcoff.rs b/src/symbolize/gimli/xcoff.rs index dd308840f..8fc3c892b 100644 --- a/src/symbolize/gimli/xcoff.rs +++ b/src/symbolize/gimli/xcoff.rs @@ -172,7 +172,7 @@ impl<'a> Object<'a> { } } - pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> { + pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> { None } } diff --git a/src/symbolize/miri.rs b/src/symbolize/miri.rs index 5b0dc3084..7999eb875 100644 --- a/src/symbolize/miri.rs +++ b/src/symbolize/miri.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use super::super::backtrace::miri::{resolve_addr, Frame}; use super::BytesOrWideString; -use super::{ResolveWhat, SymbolName}; +use super::{ResolveWhat, ShortBacktrace, SymbolName}; pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { let sym = match what { @@ -51,6 +51,10 @@ impl<'a> Symbol<'a> { core::str::from_utf8(&self.inner.inner.filename).unwrap(), )) } + + pub fn short_backtrace(&self) -> Option { + None + } } pub unsafe fn clear_symbol_cache() {} diff --git a/src/symbolize/mod.rs b/src/symbolize/mod.rs index 64542bdd0..959f08f93 100644 --- a/src/symbolize/mod.rs +++ b/src/symbolize/mod.rs @@ -251,6 +251,16 @@ impl Symbol { pub fn filename(&self) -> Option<&Path> { self.inner.filename() } + + /// Returns the short backtrace printing info for this function. + /// + /// This is currently only available when libbacktrace or gimli is being + /// used (e.g. unix platforms other) and when a binary is compiled with + /// debuginfo. If neither of these conditions is met then this will likely + /// return `None`. + pub fn short_backtrace(&self) -> Option { + self.inner.short_backtrace() + } } impl fmt::Debug for Symbol { @@ -407,6 +417,14 @@ impl<'a> fmt::Debug for SymbolName<'a> { } } +#[derive(Copy, Clone, Debug)] +#[allow(missing_docs)] +pub enum ShortBacktrace { + ThisFrameOnly, + Start, + End, +} + /// Attempt to reclaim that cached memory used to symbolicate addresses. /// /// This method will attempt to release any global data structures that have diff --git a/src/symbolize/noop.rs b/src/symbolize/noop.rs index c53336531..f6d376ad2 100644 --- a/src/symbolize/noop.rs +++ b/src/symbolize/noop.rs @@ -1,7 +1,7 @@ //! Empty symbolication strategy used to compile for platforms that have no //! support. -use super::{BytesOrWideString, ResolveWhat, SymbolName}; +use super::{BytesOrWideString, ResolveWhat, ShortBacktrace, SymbolName}; use core::ffi::c_void; use core::marker; @@ -36,6 +36,10 @@ impl Symbol<'_> { pub fn colno(&self) -> Option { None } + + pub fn short_backtrace(&self) -> Option { + None + } } pub unsafe fn clear_symbol_cache() {}