Skip to content

Commit 1b2616f

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.
1 parent 230570f commit 1b2616f

File tree

9 files changed

+107
-10
lines changed

9 files changed

+107
-10
lines changed

Cargo.lock

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

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

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ 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+
None
80+
}
7681
}
7782

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

src/symbolize/gimli.rs

+73-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use self::stash::Stash;
99
use super::BytesOrWideString;
1010
use super::ResolveWhat;
1111
use super::SymbolName;
12+
use super::ShortBacktrace;
1213
use addr2line::gimli;
14+
use addr2line::{LookupResult, LookupContinuation};
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+
const DW_AT_short_backtrace: gimli::DwAt = gimli::DwAt(0x3c00);
442+
443+
fn parse_short_backtrace<'data, R: gimli::Reader<Offset = usize>>(
444+
unit_ref: gimli::UnitRef<'_, R>,
445+
stash: &'data Stash,
446+
addr: u64,
447+
frame: &addr2line::Frame<'_, R>,
448+
) -> Option<ShortBacktrace> {
449+
use core::ops::ControlFlow;
450+
451+
let mut short_backtrace = None;
452+
unit_ref.shared_attrs(frame.dw_die_offset?, 16, |attr, _| {
453+
if attr.name() == DW_AT_short_backtrace {
454+
let parsed = ShortBacktrace::from_raw(
455+
attr.u8_value().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,32 @@ 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
498+
.and_then(|unit| parse_short_backtrace(unit, stash, addr as u64, &frame));
442499
call(Symbol::Frame {
443500
addr: addr as *mut c_void,
444501
location: frame.location,
445502
name,
503+
short_backtrace,
446504
});
447505
}
448506
}
449507
if !any_frames {
450508
if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
509+
let unit_ref = None;
451510
if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) {
452511
while let Ok(Some(frame)) = frames.next() {
453512
any_frames = true;
454513
call(Symbol::Frame {
455514
addr: addr as *mut c_void,
515+
short_backtrace: unit_ref.and_then(|unit| {
516+
parse_short_backtrace(unit, stash, object_addr, &frame)
517+
}),
456518
location: frame.location,
457519
name: frame.function.map(|f| f.name.slice()),
458520
});
@@ -475,6 +537,7 @@ pub enum Symbol<'a> {
475537
addr: *mut c_void,
476538
location: Option<addr2line::Location<'a>>,
477539
name: Option<&'a [u8]>,
540+
short_backtrace: Option<ShortBacktrace>,
478541
},
479542
/// Couldn't find debug information, but we found it in the symbol table of
480543
/// the elf executable.
@@ -532,4 +595,11 @@ impl Symbol<'_> {
532595
Symbol::Symtab { .. } => None,
533596
}
534597
}
598+
599+
pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
600+
match self {
601+
Symbol::Frame { short_backtrace, .. } => *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)