Skip to content

Commit 6fdd474

Browse files
committed
Parse short backtraces out of debuginfo
This implements the runtime side of rust-lang/compiler-team#818. - Extract a helper out of `find_frames` for iterating over a `LookupContinuation` - Make the type signature for `search_object_map` consistent across all platforms. Backtraces are inherently platform specific, but let's not make it any harder on ourselves than we have to. - Add a new `pub fn short_backtraces() -> enum { ThisFrameOnly, Start, End }` API - Use the new [`gimli::UnitRef::shared_attrs`](gimli-rs/gimli#756) API to determine whether a given frame has a short backtrace. This, for now, requires a git dependency on gimli. Note that this currently does not work on MacOS. I do not have a Mac to test this; someone lent me theirs and I tracked it down to `cx.find_dwarf_and_unit` returning `None`, but I have not had the time or resources to narrow it down further than that.
1 parent 230570f commit 6fdd474

File tree

10 files changed

+111
-11
lines changed

10 files changed

+111
-11
lines changed

Cargo.lock

+2-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ version = "0.36.0"
5151
default-features = false
5252
features = ['read_core', 'elf', 'macho', 'pe', 'xcoff', 'unaligned', 'archive']
5353

54+
[patch.crates-io]
55+
gimli = { git = "https://github.com/jyn514/gimli", branch = "shared-attrs", version = "0.31.1" }
56+
5457
[dev-dependencies]
5558
dylib-dep = { path = "crates/dylib-dep" }
5659
libloading = "0.7"

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub use self::backtrace::{trace_unsynchronized, Frame};
111111
mod backtrace;
112112

113113
pub use self::symbolize::resolve_frame_unsynchronized;
114-
pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
114+
pub use self::symbolize::{resolve_unsynchronized, ShortBacktrace, Symbol, SymbolName};
115115
mod symbolize;
116116

117117
pub use self::types::BytesOrWideString;

src/symbolize/dbghelp.rs

+6
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ impl Symbol<'_> {
7373

7474
self._filename_cache.as_ref().map(Path::new)
7575
}
76+
77+
pub fn short_backtrace(&self) -> Option<super::ShortBacktrace> {
78+
// Not supported with dllhelp API.
79+
// FIXME: might be doable with [llvm.codeview.annotation](https://reviews.llvm.org/D36904)?
80+
None
81+
}
7682
}
7783

7884
#[repr(C, align(8))]

src/symbolize/gimli.rs

+73-3
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use self::mmap::Mmap;
88
use self::stash::Stash;
99
use super::BytesOrWideString;
1010
use super::ResolveWhat;
11+
use super::ShortBacktrace;
1112
use super::SymbolName;
1213
use addr2line::gimli;
14+
use addr2line::{LookupContinuation, LookupResult};
1315
use core::convert::TryInto;
1416
use core::mem;
1517
use core::u32;
@@ -170,9 +172,24 @@ impl<'data> Context<'data> {
170172
stash: &'data Stash,
171173
probe: u64,
172174
) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
173-
use addr2line::{LookupContinuation, LookupResult};
175+
let continuation = self.dwarf.find_frames(probe);
176+
self.continuation_helper(stash, continuation)
177+
}
178+
179+
fn find_dwarf_and_unit(
180+
&'_ self,
181+
stash: &'data Stash,
182+
probe: u64,
183+
) -> Option<gimli::UnitRef<'_, EndianSlice<'data, Endian>>> {
184+
let continuation = self.dwarf.find_dwarf_and_unit(probe);
185+
self.continuation_helper(stash, continuation)
186+
}
174187

175-
let mut l = self.dwarf.find_frames(probe);
188+
fn continuation_helper<O>(
189+
&'_ self,
190+
stash: &'data Stash,
191+
mut l: LookupResult<impl LookupContinuation<Output = O, Buf = EndianSlice<'data, Endian>>>,
192+
) -> O {
176193
loop {
177194
let (load, continuation) = match l {
178195
LookupResult::Output(output) => break output,
@@ -409,6 +426,43 @@ impl Cache {
409426
}
410427
}
411428

429+
impl ShortBacktrace {
430+
fn from_raw(raw: u8) -> Option<Self> {
431+
let this = match raw {
432+
0 => ShortBacktrace::ThisFrameOnly,
433+
1 => ShortBacktrace::Start,
434+
2 => ShortBacktrace::End,
435+
_ => return None,
436+
};
437+
Some(this)
438+
}
439+
}
440+
441+
#[allow(non_upper_case_globals)]
442+
const DW_AT_short_backtrace: gimli::DwAt = gimli::DwAt(0x3c00);
443+
444+
fn parse_short_backtrace<'data, R: gimli::Reader<Offset = usize>>(
445+
unit_ref: gimli::UnitRef<'_, R>,
446+
frame: &addr2line::Frame<'_, R>,
447+
) -> Option<ShortBacktrace> {
448+
use core::ops::ControlFlow;
449+
450+
let mut short_backtrace = None;
451+
let _ = unit_ref.shared_attrs(frame.dw_die_offset?, 16, |attr, _| {
452+
if attr.name() == DW_AT_short_backtrace {
453+
let parsed = ShortBacktrace::from_raw(
454+
attr.u8_value()
455+
.ok_or(gimli::Error::UnsupportedAttributeForm)?,
456+
);
457+
short_backtrace = Some(parsed.expect("rustc generated invalid debuginfo?"));
458+
return Ok(ControlFlow::Break(()));
459+
}
460+
Ok(ControlFlow::Continue(()))
461+
});
462+
463+
short_backtrace
464+
}
465+
412466
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
413467
let addr = what.address_or_ip();
414468
let mut call = |sym: Symbol<'_>| {
@@ -435,24 +489,30 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol))
435489
if let Ok(mut frames) = cx.find_frames(stash, addr as u64) {
436490
while let Ok(Some(frame)) = frames.next() {
437491
any_frames = true;
438-
let name = match frame.function {
492+
let name = match &frame.function {
439493
Some(f) => Some(f.name.slice()),
440494
None => cx.object.search_symtab(addr as u64),
441495
};
496+
let unit_ref = cx.find_dwarf_and_unit(stash, addr as u64);
497+
let short_backtrace = unit_ref.and_then(|unit| parse_short_backtrace(unit, &frame));
442498
call(Symbol::Frame {
443499
addr: addr as *mut c_void,
444500
location: frame.location,
445501
name,
502+
short_backtrace,
446503
});
447504
}
448505
}
449506
if !any_frames {
450507
if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
508+
let unit_ref = None;
451509
if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) {
452510
while let Ok(Some(frame)) = frames.next() {
453511
any_frames = true;
454512
call(Symbol::Frame {
455513
addr: addr as *mut c_void,
514+
short_backtrace: unit_ref
515+
.and_then(|unit| parse_short_backtrace(unit, &frame)),
456516
location: frame.location,
457517
name: frame.function.map(|f| f.name.slice()),
458518
});
@@ -475,6 +535,7 @@ pub enum Symbol<'a> {
475535
addr: *mut c_void,
476536
location: Option<addr2line::Location<'a>>,
477537
name: Option<&'a [u8]>,
538+
short_backtrace: Option<ShortBacktrace>,
478539
},
479540
/// Couldn't find debug information, but we found it in the symbol table of
480541
/// the elf executable.
@@ -532,4 +593,13 @@ impl Symbol<'_> {
532593
Symbol::Symtab { .. } => None,
533594
}
534595
}
596+
597+
pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
598+
match self {
599+
Symbol::Frame {
600+
short_backtrace, ..
601+
} => *short_backtrace,
602+
Symbol::Symtab { .. } => None,
603+
}
604+
}
535605
}

src/symbolize/gimli/coff.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl<'a> Object<'a> {
100100
self.symbols[i].1.name(self.strings).ok()
101101
}
102102

103-
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
103+
pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> {
104104
None
105105
}
106106
}

src/symbolize/gimli/elf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl<'a> Object<'a> {
238238
}
239239
}
240240

241-
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
241+
pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> {
242242
None
243243
}
244244

src/symbolize/gimli/xcoff.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ impl<'a> Object<'a> {
172172
}
173173
}
174174

175-
pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
175+
pub(super) fn search_object_map(&mut self, _addr: u64) -> Option<(&Context<'_>, u64)> {
176176
None
177177
}
178178
}

src/symbolize/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ impl Symbol {
251251
pub fn filename(&self) -> Option<&Path> {
252252
self.inner.filename()
253253
}
254+
255+
/// Returns the short backtrace printing info for this function.
256+
///
257+
/// This is currently only available when libbacktrace or gimli is being
258+
/// used (e.g. unix platforms other) and when a binary is compiled with
259+
/// debuginfo. If neither of these conditions is met then this will likely
260+
/// return `None`.
261+
pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
262+
self.inner.short_backtrace()
263+
}
254264
}
255265

256266
impl fmt::Debug for Symbol {
@@ -407,6 +417,14 @@ impl<'a> fmt::Debug for SymbolName<'a> {
407417
}
408418
}
409419

420+
#[derive(Copy, Clone, Debug)]
421+
#[allow(missing_docs)]
422+
pub enum ShortBacktrace {
423+
ThisFrameOnly,
424+
Start,
425+
End,
426+
}
427+
410428
/// Attempt to reclaim that cached memory used to symbolicate addresses.
411429
///
412430
/// This method will attempt to release any global data structures that have

src/symbolize/noop.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Empty symbolication strategy used to compile for platforms that have no
22
//! support.
33
4-
use super::{BytesOrWideString, ResolveWhat, SymbolName};
4+
use super::{BytesOrWideString, ResolveWhat, ShortBacktrace, SymbolName};
55
use core::ffi::c_void;
66
use core::marker;
77

@@ -36,6 +36,10 @@ impl Symbol<'_> {
3636
pub fn colno(&self) -> Option<u32> {
3737
None
3838
}
39+
40+
pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
41+
None
42+
}
3943
}
4044

4145
pub unsafe fn clear_symbol_cache() {}

0 commit comments

Comments
 (0)