Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse short backtraces out of debuginfo #693

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/symbolize/dbghelp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ impl Symbol<'_> {

self._filename_cache.as_ref().map(Path::new)
}

pub fn short_backtrace(&self) -> Option<super::ShortBacktrace> {
// Not supported with dllhelp API.
// FIXME: might be doable with [llvm.codeview.annotation](https://reviews.llvm.org/D36904)?
None
}
}

#[repr(C, align(8))]
Expand Down
76 changes: 73 additions & 3 deletions src/symbolize/gimli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,9 +172,24 @@ impl<'data> Context<'data> {
stash: &'data Stash,
probe: u64,
) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
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<gimli::UnitRef<'_, EndianSlice<'data, Endian>>> {
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<O>(
&'_ self,
stash: &'data Stash,
mut l: LookupResult<impl LookupContinuation<Output = O, Buf = EndianSlice<'data, Endian>>>,
) -> O {
loop {
let (load, continuation) = match l {
LookupResult::Output(output) => break output,
Expand Down Expand Up @@ -409,6 +426,43 @@ impl Cache {
}
}

impl ShortBacktrace {
fn from_raw(raw: u8) -> Option<Self> {
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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this changes upstream it will also need to change here: llvm/llvm-project#123683 (comment)


fn parse_short_backtrace<'data, R: gimli::Reader<Offset = usize>>(
unit_ref: gimli::UnitRef<'_, R>,
frame: &addr2line::Frame<'_, R>,
) -> Option<ShortBacktrace> {
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<'_>| {
Expand All @@ -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()),
});
Expand All @@ -475,6 +535,7 @@ pub enum Symbol<'a> {
addr: *mut c_void,
location: Option<addr2line::Location<'a>>,
name: Option<&'a [u8]>,
short_backtrace: Option<ShortBacktrace>,
},
/// Couldn't find debug information, but we found it in the symbol table of
/// the elf executable.
Expand Down Expand Up @@ -532,4 +593,13 @@ impl Symbol<'_> {
Symbol::Symtab { .. } => None,
}
}

pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
match self {
Symbol::Frame {
short_backtrace, ..
} => *short_backtrace,
Symbol::Symtab { .. } => None,
}
}
}
2 changes: 1 addition & 1 deletion src/symbolize/gimli/coff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/symbolize/gimli/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion src/symbolize/gimli/xcoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/symbolize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ShortBacktrace> {
self.inner.short_backtrace()
}
}

impl fmt::Debug for Symbol {
Expand Down Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/symbolize/noop.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -36,6 +36,10 @@ impl Symbol<'_> {
pub fn colno(&self) -> Option<u32> {
None
}

pub fn short_backtrace(&self) -> Option<ShortBacktrace> {
None
}
}

pub unsafe fn clear_symbol_cache() {}
Loading