#![deny(missing_docs)]
#![no_std]
#[cfg(feature = "cargo-all")]
compile_error!("'--all-features' is not supported; use '--features all' instead");
#[cfg(feature = "std")]
extern crate std;
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
#[cfg(feature = "fallible-iterator")]
pub extern crate fallible_iterator;
pub extern crate gimli;
use alloc::sync::Arc;
use core::ops::ControlFlow;
use crate::function::{Function, Functions, InlinedFunction, LazyFunctions};
use crate::line::{LazyLines, LineLocationRangeIter, Lines};
use crate::lookup::{LoopingLookup, SimpleLookup};
use crate::unit::{ResUnit, ResUnits, SupUnits};
#[cfg(feature = "smallvec")]
mod maybe_small {
pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
}
#[cfg(not(feature = "smallvec"))]
mod maybe_small {
pub type Vec<T> = alloc::vec::Vec<T>;
pub type IntoIter<T> = alloc::vec::IntoIter<T>;
}
mod frame;
pub use frame::{demangle, demangle_auto, Frame, FrameIter, FunctionName, Location};
mod function;
mod lazy;
mod line;
#[cfg(feature = "loader")]
mod loader;
#[cfg(feature = "loader")]
pub use loader::{Loader, LoaderReader};
mod lookup;
pub use lookup::{LookupContinuation, LookupResult, SplitDwarfLoad};
mod unit;
pub use unit::LocationRangeIter;
type Error = gimli::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DebugFile {
Primary,
Supplementary,
Dwo,
}
pub struct Context<R: gimli::Reader> {
sections: Arc<gimli::Dwarf<R>>,
units: ResUnits<R>,
sup_units: SupUnits<R>,
}
impl<R: gimli::Reader> Context<R> {
#[allow(clippy::too_many_arguments)]
pub fn from_sections(
debug_abbrev: gimli::DebugAbbrev<R>,
debug_addr: gimli::DebugAddr<R>,
debug_aranges: gimli::DebugAranges<R>,
debug_info: gimli::DebugInfo<R>,
debug_line: gimli::DebugLine<R>,
debug_line_str: gimli::DebugLineStr<R>,
debug_ranges: gimli::DebugRanges<R>,
debug_rnglists: gimli::DebugRngLists<R>,
debug_str: gimli::DebugStr<R>,
debug_str_offsets: gimli::DebugStrOffsets<R>,
default_section: R,
) -> Result<Self, Error> {
Self::from_dwarf(gimli::Dwarf {
debug_abbrev,
debug_addr,
debug_aranges,
debug_info,
debug_line,
debug_line_str,
debug_str,
debug_str_offsets,
debug_types: default_section.clone().into(),
locations: gimli::LocationLists::new(
default_section.clone().into(),
default_section.into(),
),
ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
file_type: gimli::DwarfFileType::Main,
sup: None,
abbreviations_cache: gimli::AbbreviationsCache::new(),
})
}
#[inline]
pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
Self::from_arc_dwarf(Arc::new(sections))
}
#[inline]
pub fn from_arc_dwarf(sections: Arc<gimli::Dwarf<R>>) -> Result<Context<R>, Error> {
let units = ResUnits::parse(§ions)?;
let sup_units = if let Some(sup) = sections.sup.as_ref() {
SupUnits::parse(sup)?
} else {
SupUnits::default()
};
Ok(Context {
sections,
units,
sup_units,
})
}
}
impl<R: gimli::Reader> Context<R> {
pub fn find_dwarf_and_unit(
&self,
probe: u64,
) -> LookupResult<impl LookupContinuation<Output = Option<gimli::UnitRef<R>>, Buf = R>> {
let mut units_iter = self.units.find(probe);
if let Some(unit) = units_iter.next() {
return LoopingLookup::new_lookup(
unit.find_function_or_location(probe, self),
move |r| {
ControlFlow::Break(match r {
Ok((Some(_), _)) | Ok((_, Some(_))) => {
let (_file, unit) = unit
.dwarf_and_unit(self)
.unwrap()
.unwrap();
Some(unit)
}
_ => match units_iter.next() {
Some(next_unit) => {
return ControlFlow::Continue(
next_unit.find_function_or_location(probe, self),
);
}
None => None,
},
})
},
);
}
LoopingLookup::new_complete(None)
}
pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
for unit in self.units.find(probe) {
if let Some(location) = unit.find_location(probe, &self.sections)? {
return Ok(Some(location));
}
}
Ok(None)
}
pub fn find_location_range(
&self,
probe_low: u64,
probe_high: u64,
) -> Result<LocationRangeIter<'_, R>, Error> {
self.units
.find_location_range(probe_low, probe_high, &self.sections)
}
pub fn find_frames(
&self,
probe: u64,
) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>>
{
let mut units_iter = self.units.find(probe);
if let Some(unit) = units_iter.next() {
LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| {
ControlFlow::Break(match r {
Err(e) => Err(e),
Ok((Some(function), location)) => {
let inlined_functions = function.find_inlined_functions(probe);
Ok(FrameIter::new_frames(
unit,
&self.sections,
function,
inlined_functions,
location,
))
}
Ok((None, Some(location))) => Ok(FrameIter::new_location(location)),
Ok((None, None)) => match units_iter.next() {
Some(next_unit) => {
return ControlFlow::Continue(
next_unit.find_function_or_location(probe, self),
);
}
None => Ok(FrameIter::new_empty()),
},
})
})
} else {
LoopingLookup::new_complete(Ok(FrameIter::new_empty()))
}
}
pub fn preload_units(
&'_ self,
probe: u64,
) -> impl Iterator<
Item = (
SplitDwarfLoad<R>,
impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_,
),
> {
self.units
.find(probe)
.filter_map(move |unit| match unit.dwarf_and_unit(self) {
LookupResult::Output(_) => None,
LookupResult::Load { load, continuation } => Some((load, |result| {
continuation.resume(result).unwrap().map(|_| ())
})),
})
}
#[doc(hidden)]
pub fn parse_lines(&self) -> Result<(), Error> {
for unit in self.units.iter() {
unit.parse_lines(&self.sections)?;
}
Ok(())
}
#[doc(hidden)]
pub fn parse_functions(&self) -> Result<(), Error> {
for unit in self.units.iter() {
unit.parse_functions(self).skip_all_loads()?;
}
Ok(())
}
#[doc(hidden)]
pub fn parse_inlined_functions(&self) -> Result<(), Error> {
for unit in self.units.iter() {
unit.parse_inlined_functions(self).skip_all_loads()?;
}
Ok(())
}
}
impl<R: gimli::Reader> Context<R> {
fn find_unit(
&self,
offset: gimli::DebugInfoOffset<R::Offset>,
file: DebugFile,
) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> {
let unit = match file {
DebugFile::Primary => self.units.find_offset(offset)?,
DebugFile::Supplementary => self.sup_units.find_offset(offset)?,
DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset),
};
let unit_offset = offset
.to_unit_offset(&unit.header)
.ok_or(gimli::Error::NoEntryAtGivenOffset)?;
Ok((unit, unit_offset))
}
}
struct RangeAttributes<R: gimli::Reader> {
low_pc: Option<u64>,
high_pc: Option<u64>,
size: Option<u64>,
ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
}
impl<R: gimli::Reader> Default for RangeAttributes<R> {
fn default() -> Self {
RangeAttributes {
low_pc: None,
high_pc: None,
size: None,
ranges_offset: None,
}
}
}
impl<R: gimli::Reader> RangeAttributes<R> {
fn for_each_range<F: FnMut(gimli::Range)>(
&self,
unit: gimli::UnitRef<R>,
mut f: F,
) -> Result<bool, Error> {
let mut added_any = false;
let mut add_range = |range: gimli::Range| {
if range.begin < range.end {
f(range);
added_any = true
}
};
if let Some(ranges_offset) = self.ranges_offset {
let mut range_list = unit.ranges(ranges_offset)?;
while let Some(range) = range_list.next()? {
add_range(range);
}
} else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
add_range(gimli::Range { begin, end });
} else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
let end = begin.wrapping_add(size);
add_range(gimli::Range { begin, end });
}
Ok(added_any)
}
}
#[cfg(test)]
mod tests {
#[test]
fn context_is_send() {
fn assert_is_send<T: Send>() {}
assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>();
}
}