aboutsummaryrefslogtreecommitdiff
path: root/vendor/addr2line/src/lib.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
committerValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
commita990de90fe41456a23e58bd087d2f107d321f3a1 (patch)
tree15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/addr2line/src/lib.rs
parent3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff)
downloadfparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz
fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip
Deleted vendor folder
Diffstat (limited to 'vendor/addr2line/src/lib.rs')
-rw-r--r--vendor/addr2line/src/lib.rs1729
1 files changed, 0 insertions, 1729 deletions
diff --git a/vendor/addr2line/src/lib.rs b/vendor/addr2line/src/lib.rs
deleted file mode 100644
index 3270aad..0000000
--- a/vendor/addr2line/src/lib.rs
+++ /dev/null
@@ -1,1729 +0,0 @@
-//! This crate provides a cross-platform library and binary for translating addresses into
-//! function names, file names and line numbers. Given an address in an executable or an
-//! offset in a section of a relocatable object, it uses the debugging information to
-//! figure out which file name and line number are associated with it.
-//!
-//! When used as a library, files must first be loaded using the
-//! [`object`](https://github.com/gimli-rs/object) crate.
-//! A context can then be created with [`Context::new`](./struct.Context.html#method.new).
-//! The context caches some of the parsed information so that multiple lookups are
-//! efficient.
-//! Location information is obtained with
-//! [`Context::find_location`](./struct.Context.html#method.find_location) or
-//! [`Context::find_location_range`](./struct.Context.html#method.find_location_range).
-//! Function information is obtained with
-//! [`Context::find_frames`](./struct.Context.html#method.find_frames), which returns
-//! a frame for each inline function. Each frame contains both name and location.
-//!
-//! The crate has an example CLI wrapper around the library which provides some of
-//! the functionality of the `addr2line` command line tool distributed with [GNU
-//! binutils](https://www.gnu.org/software/binutils/).
-//!
-//! Currently this library only provides information from the DWARF debugging information,
-//! which is parsed using [`gimli`](https://github.com/gimli-rs/gimli). The example CLI
-//! wrapper also uses symbol table information provided by the `object` crate.
-#![deny(missing_docs)]
-#![no_std]
-
-#[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;
-#[cfg(feature = "object")]
-pub extern crate object;
-
-use alloc::borrow::Cow;
-use alloc::boxed::Box;
-#[cfg(feature = "object")]
-use alloc::rc::Rc;
-use alloc::string::{String, ToString};
-use alloc::sync::Arc;
-use alloc::vec::Vec;
-
-use core::cmp::{self, Ordering};
-use core::iter;
-use core::marker::PhantomData;
-use core::mem;
-use core::num::NonZeroU64;
-use core::ops::ControlFlow;
-use core::u64;
-
-use crate::function::{Function, Functions, InlinedFunction};
-use crate::lazy::LazyCell;
-
-#[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>;
-}
-
-#[cfg(all(feature = "std", feature = "object", feature = "memmap2"))]
-/// A simple builtin split DWARF loader.
-pub mod builtin_split_dwarf_loader;
-mod function;
-mod lazy;
-
-type Error = gimli::Error;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum DebugFile {
- Primary,
- Supplementary,
- Dwo,
-}
-
-/// Operations that consult debug information may require additional files
-/// to be loaded if split DWARF is being used. This enum returns the result
-/// of the operation in the `Break` variant, or information about the split
-/// DWARF that is required and a continuation to invoke once it is available
-/// in the `Continue` variant.
-///
-/// This enum is intended to be used in a loop like so:
-/// ```no_run
-/// # use addr2line::*;
-/// # use std::sync::Arc;
-/// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
-/// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
-/// const ADDRESS: u64 = 0xdeadbeef;
-/// let mut r = ctx.find_frames(ADDRESS);
-/// let result = loop {
-/// match r {
-/// LookupResult::Output(result) => break result,
-/// LookupResult::Load { load, continuation } => {
-/// let dwo = do_split_dwarf_load(load);
-/// r = continuation.resume(dwo);
-/// }
-/// }
-/// };
-/// ```
-pub enum LookupResult<L: LookupContinuation> {
- /// The lookup requires split DWARF data to be loaded.
- Load {
- /// The information needed to find the split DWARF data.
- load: SplitDwarfLoad<<L as LookupContinuation>::Buf>,
- /// The continuation to resume with the loaded split DWARF data.
- continuation: L,
- },
- /// The lookup has completed and produced an output.
- Output(<L as LookupContinuation>::Output),
-}
-
-/// This trait represents a partially complete operation that can be resumed
-/// once a load of needed split DWARF data is completed or abandoned by the
-/// API consumer.
-pub trait LookupContinuation: Sized {
- /// The final output of this operation.
- type Output;
- /// The type of reader used.
- type Buf: gimli::Reader;
-
- /// Resumes the operation with the provided data.
- ///
- /// After the caller loads the split DWARF data required, call this
- /// method to resume the operation. The return value of this method
- /// indicates if the computation has completed or if further data is
- /// required.
- ///
- /// If the additional data cannot be located, or the caller does not
- /// support split DWARF, `resume(None)` can be used to continue the
- /// operation with the data that is available.
- fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>;
-}
-
-impl<L: LookupContinuation> LookupResult<L> {
- /// Callers that do not handle split DWARF can call `skip_all_loads`
- /// to fast-forward to the end result. This result is produced with
- /// the data that is available and may be less accurate than the
- /// the results that would be produced if the caller did properly
- /// support split DWARF.
- pub fn skip_all_loads(mut self) -> L::Output {
- loop {
- self = match self {
- LookupResult::Output(t) => return t,
- LookupResult::Load { continuation, .. } => continuation.resume(None),
- };
- }
- }
-
- fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>> {
- match self {
- LookupResult::Output(t) => LookupResult::Output(f(t)),
- LookupResult::Load { load, continuation } => LookupResult::Load {
- load,
- continuation: MappedLookup {
- original: continuation,
- mutator: f,
- },
- },
- }
- }
-
- fn unwrap(self) -> L::Output {
- match self {
- LookupResult::Output(t) => t,
- LookupResult::Load { .. } => unreachable!("Internal API misuse"),
- }
- }
-}
-
-/// The state necessary to perform address to line translation.
-///
-/// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
-/// when performing lookups for many addresses in the same executable.
-pub struct Context<R: gimli::Reader> {
- sections: Arc<gimli::Dwarf<R>>,
- unit_ranges: Box<[UnitRange]>,
- units: Box<[ResUnit<R>]>,
- sup_units: Box<[SupUnit<R>]>,
-}
-
-/// The type of `Context` that supports the `new` method.
-#[cfg(feature = "std-object")]
-pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>;
-
-#[cfg(feature = "std-object")]
-impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> {
- /// Construct a new `Context`.
- ///
- /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
- /// This means it is not thread safe, has no lifetime constraints (since it copies
- /// the input data), and works for any endianity.
- ///
- /// Performance sensitive applications may want to use `Context::from_dwarf`
- /// with a more specialised `gimli::Reader` implementation.
- #[inline]
- pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>(
- file: &'file O,
- ) -> Result<Self, Error> {
- Self::new_with_sup(file, None)
- }
-
- /// Construct a new `Context`.
- ///
- /// Optionally also use a supplementary object file.
- ///
- /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
- /// This means it is not thread safe, has no lifetime constraints (since it copies
- /// the input data), and works for any endianity.
- ///
- /// Performance sensitive applications may want to use `Context::from_dwarf`
- /// with a more specialised `gimli::Reader` implementation.
- pub fn new_with_sup<'data: 'file, 'file, O: object::Object<'data, 'file>>(
- file: &'file O,
- sup_file: Option<&'file O>,
- ) -> Result<Self, Error> {
- let endian = if file.is_little_endian() {
- gimli::RunTimeEndian::Little
- } else {
- gimli::RunTimeEndian::Big
- };
-
- fn load_section<'data: 'file, 'file, O, Endian>(
- id: gimli::SectionId,
- file: &'file O,
- endian: Endian,
- ) -> Result<gimli::EndianRcSlice<Endian>, Error>
- where
- O: object::Object<'data, 'file>,
- Endian: gimli::Endianity,
- {
- use object::ObjectSection;
-
- let data = file
- .section_by_name(id.name())
- .and_then(|section| section.uncompressed_data().ok())
- .unwrap_or(Cow::Borrowed(&[]));
- Ok(gimli::EndianRcSlice::new(Rc::from(&*data), endian))
- }
-
- let mut dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian))?;
- if let Some(sup_file) = sup_file {
- dwarf.load_sup(|id| load_section(id, sup_file, endian))?;
- }
- Context::from_dwarf(dwarf)
- }
-}
-
-impl<R: gimli::Reader> Context<R> {
- /// Construct a new `Context` from DWARF sections.
- ///
- /// This method does not support using a supplementary object file.
- 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(),
- })
- }
-
- /// Construct a new `Context` from an existing [`gimli::Dwarf`] object.
- #[inline]
- pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
- let sections = Arc::new(sections);
- let (unit_ranges, units) = Context::parse_units(&sections)?;
- let sup_units = if let Some(sup) = sections.sup.as_ref() {
- Context::parse_sup(sup)?
- } else {
- Vec::new()
- };
- Ok(Context {
- sections,
- unit_ranges: unit_ranges.into_boxed_slice(),
- units: units.into_boxed_slice(),
- sup_units: sup_units.into_boxed_slice(),
- })
- }
-
- /// Finds the CUs for the function address given.
- ///
- /// There might be multiple CUs whose range contains this address.
- /// Weak symbols have shown up in the wild which cause this to happen
- /// but otherwise this can happen if the CU has non-contiguous functions
- /// but only reports a single range.
- ///
- /// Consequently we return an iterator for all CUs which may contain the
- /// address, and the caller must check if there is actually a function or
- /// location in the CU for that address.
- fn find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> {
- self.find_units_range(probe, probe + 1)
- .map(|(unit, _range)| unit)
- }
-
- /// Finds the CUs covering the range of addresses given.
- ///
- /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple
- /// ranges for the same unit.
- #[inline]
- fn find_units_range(
- &self,
- probe_low: u64,
- probe_high: u64,
- ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> {
- // First up find the position in the array which could have our function
- // address.
- let pos = match self
- .unit_ranges
- .binary_search_by_key(&probe_high, |i| i.range.begin)
- {
- // Although unlikely, we could find an exact match.
- Ok(i) => i + 1,
- // No exact match was found, but this probe would fit at slot `i`.
- // This means that slot `i` is bigger than `probe`, along with all
- // indices greater than `i`, so we need to search all previous
- // entries.
- Err(i) => i,
- };
-
- // Once we have our index we iterate backwards from that position
- // looking for a matching CU.
- self.unit_ranges[..pos]
- .iter()
- .rev()
- .take_while(move |i| {
- // We know that this CU's start is beneath the probe already because
- // of our sorted array.
- debug_assert!(i.range.begin <= probe_high);
-
- // Each entry keeps track of the maximum end address seen so far,
- // starting from the beginning of the array of unit ranges. We're
- // iterating in reverse so if our probe is beyond the maximum range
- // of this entry, then it's guaranteed to not fit in any prior
- // entries, so we break out.
- probe_low < i.max_end
- })
- .filter_map(move |i| {
- // If this CU doesn't actually contain this address, move to the
- // next CU.
- if probe_low >= i.range.end || probe_high <= i.range.begin {
- return None;
- }
- Some((&self.units[i.unit_id], &i.range))
- })
- }
-
- /// Find the DWARF unit corresponding to the given virtual memory address.
- pub fn find_dwarf_and_unit(
- &self,
- probe: u64,
- ) -> LookupResult<
- impl LookupContinuation<Output = Option<(&gimli::Dwarf<R>, &gimli::Unit<R>)>, Buf = R>,
- > {
- let mut units_iter = self.find_units(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, sections, unit) = unit
- .dwarf_and_unit_dwo(self)
- // We've already been through both error cases here to get to this point.
- .unwrap()
- .unwrap();
- Some((sections, 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)
- }
-
- /// Find the source file and line corresponding to the given virtual memory address.
- pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
- for unit in self.find_units(probe) {
- if let Some(location) = unit.find_location(probe, &self.sections)? {
- return Ok(Some(location));
- }
- }
- Ok(None)
- }
-
- /// Return source file and lines for a range of addresses. For each location it also
- /// returns the address and size of the range of the underlying instructions.
- pub fn find_location_range(
- &self,
- probe_low: u64,
- probe_high: u64,
- ) -> Result<LocationRangeIter<'_, R>, Error> {
- LocationRangeIter::new(self, probe_low, probe_high)
- }
-
- /// Return an iterator for the function frames corresponding to the given virtual
- /// memory address.
- ///
- /// If the probe address is not for an inline function then only one frame is
- /// returned.
- ///
- /// If the probe address is for an inline function then the first frame corresponds
- /// to the innermost inline function. Subsequent frames contain the caller and call
- /// location, until an non-inline caller is reached.
- pub fn find_frames(
- &self,
- probe: u64,
- ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>>
- {
- let mut units_iter = self.find_units(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(FrameIterState::Frames(FrameIterFrames {
- unit,
- sections: &self.sections,
- function,
- inlined_functions,
- next: location,
- })))
- }
- Ok((None, Some(location))) => {
- Ok(FrameIter(FrameIterState::Location(Some(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(FrameIterState::Empty)),
- },
- })
- })
- } else {
- LoopingLookup::new_complete(Ok(FrameIter(FrameIterState::Empty)))
- }
- }
-
- /// Preload units for `probe`.
- ///
- /// The iterator returns pairs of `SplitDwarfLoad`s containing the
- /// information needed to locate and load split DWARF for `probe` and
- /// a matching callback to invoke once that data is available.
- ///
- /// If this method is called, and all of the returned closures are invoked,
- /// addr2line guarantees that any future API call for the address `probe`
- /// will not require the loading of any split DWARF.
- ///
- /// ```no_run
- /// # use addr2line::*;
- /// # use std::sync::Arc;
- /// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
- /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
- /// const ADDRESS: u64 = 0xdeadbeef;
- /// ctx.preload_units(ADDRESS).for_each(|(load, callback)| {
- /// let dwo = do_split_dwarf_load(load);
- /// callback(dwo);
- /// });
- ///
- /// let frames_iter = match ctx.find_frames(ADDRESS) {
- /// LookupResult::Output(result) => result,
- /// LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"),
- /// };
- ///
- /// // ...
- /// ```
- pub fn preload_units(
- &'_ self,
- probe: u64,
- ) -> impl Iterator<
- Item = (
- SplitDwarfLoad<R>,
- impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_,
- ),
- > {
- self.find_units(probe)
- .filter_map(move |unit| match unit.dwarf_and_unit_dwo(self) {
- LookupResult::Output(_) => None,
- LookupResult::Load { load, continuation } => Some((load, |result| {
- continuation.resume(result).unwrap().map(|_| ())
- })),
- })
- }
-
- /// Initialize all line data structures. This is used for benchmarks.
- #[doc(hidden)]
- pub fn parse_lines(&self) -> Result<(), Error> {
- for unit in self.units.iter() {
- unit.parse_lines(&self.sections)?;
- }
- Ok(())
- }
-
- /// Initialize all function data structures. This is used for benchmarks.
- #[doc(hidden)]
- pub fn parse_functions(&self) -> Result<(), Error> {
- for unit in self.units.iter() {
- unit.parse_functions(self).skip_all_loads()?;
- }
- Ok(())
- }
-
- /// Initialize all inlined function data structures. This is used for benchmarks.
- #[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(())
- }
-}
-
-struct UnitRange {
- unit_id: usize,
- max_end: u64,
- range: gimli::Range,
-}
-
-struct ResUnit<R: gimli::Reader> {
- offset: gimli::DebugInfoOffset<R::Offset>,
- dw_unit: gimli::Unit<R>,
- lang: Option<gimli::DwLang>,
- lines: LazyCell<Result<Lines, Error>>,
- funcs: LazyCell<Result<Functions<R>, Error>>,
- dwo: LazyCell<Result<Option<Box<(Arc<gimli::Dwarf<R>>, gimli::Unit<R>)>>, Error>>,
-}
-
-struct SupUnit<R: gimli::Reader> {
- offset: gimli::DebugInfoOffset<R::Offset>,
- dw_unit: gimli::Unit<R>,
-}
-
-impl<R: gimli::Reader> Context<R> {
- fn parse_units(sections: &gimli::Dwarf<R>) -> Result<(Vec<UnitRange>, Vec<ResUnit<R>>), Error> {
- // Find all the references to compilation units in .debug_aranges.
- // Note that we always also iterate through all of .debug_info to
- // find compilation units, because .debug_aranges may be missing some.
- let mut aranges = Vec::new();
- let mut headers = sections.debug_aranges.headers();
- while let Some(header) = headers.next()? {
- aranges.push((header.debug_info_offset(), header.offset()));
- }
- aranges.sort_by_key(|i| i.0);
-
- let mut unit_ranges = Vec::new();
- let mut res_units = Vec::new();
- let mut units = sections.units();
- while let Some(header) = units.next()? {
- let unit_id = res_units.len();
- let offset = match header.offset().as_debug_info_offset() {
- Some(offset) => offset,
- None => continue,
- };
- // We mainly want compile units, but we may need to follow references to entries
- // within other units for function names. We don't need anything from type units.
- match header.type_() {
- gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue,
- _ => {}
- }
- let dw_unit = match sections.unit(header) {
- Ok(dw_unit) => dw_unit,
- Err(_) => continue,
- };
-
- let mut lang = None;
- let mut have_unit_range = false;
- {
- let mut entries = dw_unit.entries_raw(None)?;
-
- let abbrev = match entries.read_abbreviation()? {
- Some(abbrev) => abbrev,
- None => continue,
- };
-
- let mut ranges = RangeAttributes::default();
- for spec in abbrev.attributes() {
- let attr = entries.read_attribute(*spec)?;
- match attr.name() {
- gimli::DW_AT_low_pc => match attr.value() {
- gimli::AttributeValue::Addr(val) => ranges.low_pc = Some(val),
- gimli::AttributeValue::DebugAddrIndex(index) => {
- ranges.low_pc = Some(sections.address(&dw_unit, index)?);
- }
- _ => {}
- },
- gimli::DW_AT_high_pc => match attr.value() {
- gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
- gimli::AttributeValue::DebugAddrIndex(index) => {
- ranges.high_pc = Some(sections.address(&dw_unit, index)?);
- }
- gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
- _ => {}
- },
- gimli::DW_AT_ranges => {
- ranges.ranges_offset =
- sections.attr_ranges_offset(&dw_unit, attr.value())?;
- }
- gimli::DW_AT_language => {
- if let gimli::AttributeValue::Language(val) = attr.value() {
- lang = Some(val);
- }
- }
- _ => {}
- }
- }
-
- // Find the address ranges for the CU, using in order of preference:
- // - DW_AT_ranges
- // - .debug_aranges
- // - DW_AT_low_pc/DW_AT_high_pc
- //
- // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice,
- // but the feeling is that DW_AT_ranges is more likely to be reliable or complete
- // if it is present.
- //
- // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because
- // it has been observed on macOS that DW_AT_ranges was not emitted even for
- // discontiguous CUs.
- let i = match ranges.ranges_offset {
- Some(_) => None,
- None => aranges.binary_search_by_key(&offset, |x| x.0).ok(),
- };
- if let Some(mut i) = i {
- // There should be only one set per CU, but in practice multiple
- // sets have been observed. This is probably a compiler bug, but
- // either way we need to handle it.
- while i > 0 && aranges[i - 1].0 == offset {
- i -= 1;
- }
- for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) {
- let aranges_header = sections.debug_aranges.header(*aranges_offset)?;
- let mut aranges = aranges_header.entries();
- while let Some(arange) = aranges.next()? {
- if arange.length() != 0 {
- unit_ranges.push(UnitRange {
- range: arange.range(),
- unit_id,
- max_end: 0,
- });
- have_unit_range = true;
- }
- }
- }
- } else {
- have_unit_range |= ranges.for_each_range(sections, &dw_unit, |range| {
- unit_ranges.push(UnitRange {
- range,
- unit_id,
- max_end: 0,
- });
- })?;
- }
- }
-
- let lines = LazyCell::new();
- if !have_unit_range {
- // The unit did not declare any ranges.
- // Try to get some ranges from the line program sequences.
- if let Some(ref ilnp) = dw_unit.line_program {
- if let Ok(lines) = lines
- .borrow_with(|| Lines::parse(&dw_unit, ilnp.clone(), sections))
- .as_ref()
- {
- for sequence in lines.sequences.iter() {
- unit_ranges.push(UnitRange {
- range: gimli::Range {
- begin: sequence.start,
- end: sequence.end,
- },
- unit_id,
- max_end: 0,
- })
- }
- }
- }
- }
-
- res_units.push(ResUnit {
- offset,
- dw_unit,
- lang,
- lines,
- funcs: LazyCell::new(),
- dwo: LazyCell::new(),
- });
- }
-
- // Sort this for faster lookup in `find_unit_and_address` below.
- unit_ranges.sort_by_key(|i| i.range.begin);
-
- // Calculate the `max_end` field now that we've determined the order of
- // CUs.
- let mut max = 0;
- for i in unit_ranges.iter_mut() {
- max = max.max(i.range.end);
- i.max_end = max;
- }
-
- Ok((unit_ranges, res_units))
- }
-
- fn parse_sup(sections: &gimli::Dwarf<R>) -> Result<Vec<SupUnit<R>>, Error> {
- let mut sup_units = Vec::new();
- let mut units = sections.units();
- while let Some(header) = units.next()? {
- let offset = match header.offset().as_debug_info_offset() {
- Some(offset) => offset,
- None => continue,
- };
- let dw_unit = match sections.unit(header) {
- Ok(dw_unit) => dw_unit,
- Err(_) => continue,
- };
- sup_units.push(SupUnit { dw_unit, offset });
- }
- Ok(sup_units)
- }
-
- // Find the unit containing the given offset, and convert the offset into a unit offset.
- 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 => {
- match self
- .units
- .binary_search_by_key(&offset.0, |unit| unit.offset.0)
- {
- // There is never a DIE at the unit offset or before the first unit.
- Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
- Err(i) => &self.units[i - 1].dw_unit,
- }
- }
- DebugFile::Supplementary => {
- match self
- .sup_units
- .binary_search_by_key(&offset.0, |unit| unit.offset.0)
- {
- // There is never a DIE at the unit offset or before the first unit.
- Ok(_) | Err(0) => return Err(gimli::Error::NoEntryAtGivenOffset),
- Err(i) => &self.sup_units[i - 1].dw_unit,
- }
- }
- 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 Lines {
- files: Box<[String]>,
- sequences: Box<[LineSequence]>,
-}
-
-impl Lines {
- fn parse<R: gimli::Reader>(
- dw_unit: &gimli::Unit<R>,
- ilnp: gimli::IncompleteLineProgram<R, R::Offset>,
- sections: &gimli::Dwarf<R>,
- ) -> Result<Self, Error> {
- let mut sequences = Vec::new();
- let mut sequence_rows = Vec::<LineRow>::new();
- let mut rows = ilnp.rows();
- while let Some((_, row)) = rows.next_row()? {
- if row.end_sequence() {
- if let Some(start) = sequence_rows.first().map(|x| x.address) {
- let end = row.address();
- let mut rows = Vec::new();
- mem::swap(&mut rows, &mut sequence_rows);
- sequences.push(LineSequence {
- start,
- end,
- rows: rows.into_boxed_slice(),
- });
- }
- continue;
- }
-
- let address = row.address();
- let file_index = row.file_index();
- let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32;
- let column = match row.column() {
- gimli::ColumnType::LeftEdge => 0,
- gimli::ColumnType::Column(x) => x.get() as u32,
- };
-
- if let Some(last_row) = sequence_rows.last_mut() {
- if last_row.address == address {
- last_row.file_index = file_index;
- last_row.line = line;
- last_row.column = column;
- continue;
- }
- }
-
- sequence_rows.push(LineRow {
- address,
- file_index,
- line,
- column,
- });
- }
- sequences.sort_by_key(|x| x.start);
-
- let mut files = Vec::new();
- let header = rows.header();
- match header.file(0) {
- Some(file) => files.push(render_file(dw_unit, file, header, sections)?),
- None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index
- }
- let mut index = 1;
- while let Some(file) = header.file(index) {
- files.push(render_file(dw_unit, file, header, sections)?);
- index += 1;
- }
-
- Ok(Self {
- files: files.into_boxed_slice(),
- sequences: sequences.into_boxed_slice(),
- })
- }
-}
-
-fn render_file<R: gimli::Reader>(
- dw_unit: &gimli::Unit<R>,
- file: &gimli::FileEntry<R, R::Offset>,
- header: &gimli::LineProgramHeader<R, R::Offset>,
- sections: &gimli::Dwarf<R>,
-) -> Result<String, gimli::Error> {
- let mut path = if let Some(ref comp_dir) = dw_unit.comp_dir {
- comp_dir.to_string_lossy()?.into_owned()
- } else {
- String::new()
- };
-
- // The directory index 0 is defined to correspond to the compilation unit directory.
- if file.directory_index() != 0 {
- if let Some(directory) = file.directory(header) {
- path_push(
- &mut path,
- sections
- .attr_string(dw_unit, directory)?
- .to_string_lossy()?
- .as_ref(),
- );
- }
- }
-
- path_push(
- &mut path,
- sections
- .attr_string(dw_unit, file.path_name())?
- .to_string_lossy()?
- .as_ref(),
- );
-
- Ok(path)
-}
-
-struct LineSequence {
- start: u64,
- end: u64,
- rows: Box<[LineRow]>,
-}
-
-struct LineRow {
- address: u64,
- file_index: u64,
- line: u32,
- column: u32,
-}
-
-/// This struct contains the information needed to find split DWARF data
-/// and to produce a `gimli::Dwarf<R>` for it.
-pub struct SplitDwarfLoad<R> {
- /// The dwo id, for looking up in a DWARF package, or for
- /// verifying an unpacked dwo found on the file system
- pub dwo_id: gimli::DwoId,
- /// The compilation directory `path` is relative to.
- pub comp_dir: Option<R>,
- /// A path on the filesystem, relative to `comp_dir` to find this dwo.
- pub path: Option<R>,
- /// Once the split DWARF data is loaded, the loader is expected
- /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before
- /// returning the data.
- pub parent: Arc<gimli::Dwarf<R>>,
-}
-
-struct SimpleLookup<T, R, F>
-where
- F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
- R: gimli::Reader,
-{
- f: F,
- phantom: PhantomData<(T, R)>,
-}
-
-impl<T, R, F> SimpleLookup<T, R, F>
-where
- F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
- R: gimli::Reader,
-{
- fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> {
- LookupResult::Output(t)
- }
-
- fn new_needs_load(load: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> {
- LookupResult::Load {
- load,
- continuation: SimpleLookup {
- f,
- phantom: PhantomData,
- },
- }
- }
-}
-
-impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F>
-where
- F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
- R: gimli::Reader,
-{
- type Output = T;
- type Buf = R;
-
- fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
- LookupResult::Output((self.f)(v))
- }
-}
-
-struct MappedLookup<T, L, F>
-where
- L: LookupContinuation,
- F: FnOnce(L::Output) -> T,
-{
- original: L,
- mutator: F,
-}
-
-impl<T, L, F> LookupContinuation for MappedLookup<T, L, F>
-where
- L: LookupContinuation,
- F: FnOnce(L::Output) -> T,
-{
- type Output = T;
- type Buf = L::Buf;
-
- fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
- match self.original.resume(v) {
- LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)),
- LookupResult::Load { load, continuation } => LookupResult::Load {
- load,
- continuation: MappedLookup {
- original: continuation,
- mutator: self.mutator,
- },
- },
- }
- }
-}
-
-/// Some functions (e.g. `find_frames`) require considering multiple
-/// compilation units, each of which might require their own split DWARF
-/// lookup (and thus produce a continuation).
-///
-/// We store the underlying continuation here as well as a mutator function
-/// that will either a) decide that the result of this continuation is
-/// what is needed and mutate it to the final result or b) produce another
-/// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation
-/// `LookupResult` with successive invocations of the mutator, until a new
-/// continuation or a final result is produced. And finally, the impl of
-/// `LookupContinuation::resume` will call `new_lookup` each time the
-/// computation is resumed.
-struct LoopingLookup<T, L, F>
-where
- L: LookupContinuation,
- F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
-{
- continuation: L,
- mutator: F,
-}
-
-impl<T, L, F> LoopingLookup<T, L, F>
-where
- L: LookupContinuation,
- F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
-{
- fn new_complete(t: T) -> LookupResult<Self> {
- LookupResult::Output(t)
- }
-
- fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> {
- // Drive the loop eagerly so that we only ever have to represent one state
- // (the r == ControlFlow::Continue state) in LoopingLookup.
- loop {
- match r {
- LookupResult::Output(l) => match mutator(l) {
- ControlFlow::Break(t) => return LookupResult::Output(t),
- ControlFlow::Continue(r2) => {
- r = r2;
- }
- },
- LookupResult::Load { load, continuation } => {
- return LookupResult::Load {
- load,
- continuation: LoopingLookup {
- continuation,
- mutator,
- },
- };
- }
- }
- }
- }
-}
-
-impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F>
-where
- L: LookupContinuation,
- F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
-{
- type Output = T;
- type Buf = L::Buf;
-
- fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
- let r = self.continuation.resume(v);
- LoopingLookup::new_lookup(r, self.mutator)
- }
-}
-
-impl<R: gimli::Reader> ResUnit<R> {
- fn dwarf_and_unit_dwo<'unit, 'ctx: 'unit>(
- &'unit self,
- ctx: &'ctx Context<R>,
- ) -> LookupResult<
- SimpleLookup<
- Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
- R,
- impl FnOnce(
- Option<Arc<gimli::Dwarf<R>>>,
- )
- -> Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>,
- >,
- > {
- loop {
- break SimpleLookup::new_complete(match self.dwo.borrow() {
- Some(Ok(Some(v))) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
- Some(Ok(None)) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
- Some(Err(e)) => Err(*e),
- None => {
- let dwo_id = match self.dw_unit.dwo_id {
- None => {
- self.dwo.borrow_with(|| Ok(None));
- continue;
- }
- Some(dwo_id) => dwo_id,
- };
-
- let comp_dir = self.dw_unit.comp_dir.clone();
-
- let dwo_name = self.dw_unit.dwo_name().and_then(|s| {
- if let Some(s) = s {
- Ok(Some(ctx.sections.attr_string(&self.dw_unit, s)?))
- } else {
- Ok(None)
- }
- });
-
- let path = match dwo_name {
- Ok(v) => v,
- Err(e) => {
- self.dwo.borrow_with(|| Err(e));
- continue;
- }
- };
-
- let process_dwo = move |dwo_dwarf: Option<Arc<gimli::Dwarf<R>>>| {
- let dwo_dwarf = match dwo_dwarf {
- None => return Ok(None),
- Some(dwo_dwarf) => dwo_dwarf,
- };
- let mut dwo_units = dwo_dwarf.units();
- let dwo_header = match dwo_units.next()? {
- Some(dwo_header) => dwo_header,
- None => return Ok(None),
- };
-
- let mut dwo_unit = dwo_dwarf.unit(dwo_header)?;
- dwo_unit.copy_relocated_attributes(&self.dw_unit);
- Ok(Some(Box::new((dwo_dwarf, dwo_unit))))
- };
-
- return SimpleLookup::new_needs_load(
- SplitDwarfLoad {
- dwo_id,
- comp_dir,
- path,
- parent: ctx.sections.clone(),
- },
- move |dwo_dwarf| match self.dwo.borrow_with(|| process_dwo(dwo_dwarf)) {
- Ok(Some(v)) => Ok((DebugFile::Dwo, &*v.0, &v.1)),
- Ok(None) => Ok((DebugFile::Primary, &*ctx.sections, &self.dw_unit)),
- Err(e) => Err(*e),
- },
- );
- }
- });
- }
- }
-
- fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> {
- // NB: line information is always stored in the main debug file so this does not need
- // to handle DWOs.
- let ilnp = match self.dw_unit.line_program {
- Some(ref ilnp) => ilnp,
- None => return Ok(None),
- };
- self.lines
- .borrow_with(|| Lines::parse(&self.dw_unit, ilnp.clone(), sections))
- .as_ref()
- .map(Some)
- .map_err(Error::clone)
- }
-
- fn parse_functions_dwarf_and_unit(
- &self,
- unit: &gimli::Unit<R>,
- sections: &gimli::Dwarf<R>,
- ) -> Result<&Functions<R>, Error> {
- self.funcs
- .borrow_with(|| Functions::parse(unit, sections))
- .as_ref()
- .map_err(Error::clone)
- }
-
- fn parse_functions<'unit, 'ctx: 'unit>(
- &'unit self,
- ctx: &'ctx Context<R>,
- ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>>
- {
- self.dwarf_and_unit_dwo(ctx).map(move |r| {
- let (_file, sections, unit) = r?;
- self.parse_functions_dwarf_and_unit(unit, sections)
- })
- }
- fn parse_inlined_functions<'unit, 'ctx: 'unit>(
- &'unit self,
- ctx: &'ctx Context<R>,
- ) -> LookupResult<impl LookupContinuation<Output = Result<(), Error>, Buf = R> + 'unit> {
- self.dwarf_and_unit_dwo(ctx).map(move |r| {
- let (file, sections, unit) = r?;
- self.funcs
- .borrow_with(|| Functions::parse(unit, sections))
- .as_ref()
- .map_err(Error::clone)?
- .parse_inlined_functions(file, unit, ctx, sections)
- })
- }
-
- fn find_location(
- &self,
- probe: u64,
- sections: &gimli::Dwarf<R>,
- ) -> Result<Option<Location<'_>>, Error> {
- if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? {
- match iter.next() {
- None => Ok(None),
- Some((_addr, _len, loc)) => Ok(Some(loc)),
- }
- } else {
- Ok(None)
- }
- }
-
- #[inline]
- fn find_location_range(
- &self,
- probe_low: u64,
- probe_high: u64,
- sections: &gimli::Dwarf<R>,
- ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> {
- LocationRangeUnitIter::new(self, sections, probe_low, probe_high)
- }
-
- fn find_function_or_location<'unit, 'ctx: 'unit>(
- &'unit self,
- probe: u64,
- ctx: &'ctx Context<R>,
- ) -> LookupResult<
- impl LookupContinuation<
- Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>,
- Buf = R,
- >,
- > {
- self.dwarf_and_unit_dwo(ctx).map(move |r| {
- let (file, sections, unit) = r?;
- let functions = self.parse_functions_dwarf_and_unit(unit, sections)?;
- let function = match functions.find_address(probe) {
- Some(address) => {
- let function_index = functions.addresses[address].function;
- let (offset, ref function) = functions.functions[function_index];
- Some(
- function
- .borrow_with(|| Function::parse(offset, file, unit, ctx, sections))
- .as_ref()
- .map_err(Error::clone)?,
- )
- }
- None => None,
- };
- let location = self.find_location(probe, sections)?;
- Ok((function, location))
- })
- }
-}
-
-/// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`.
-pub struct LocationRangeIter<'ctx, R: gimli::Reader> {
- unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>,
- iter: Option<LocationRangeUnitIter<'ctx>>,
-
- probe_low: u64,
- probe_high: u64,
- sections: &'ctx gimli::Dwarf<R>,
-}
-
-impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> {
- #[inline]
- fn new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error> {
- let sections = &ctx.sections;
- let unit_iter = ctx.find_units_range(probe_low, probe_high);
- Ok(Self {
- unit_iter: Box::new(unit_iter),
- iter: None,
- probe_low,
- probe_high,
- sections,
- })
- }
-
- fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> {
- loop {
- let iter = self.iter.take();
- match iter {
- None => match self.unit_iter.next() {
- Some((unit, range)) => {
- self.iter = unit.find_location_range(
- cmp::max(self.probe_low, range.begin),
- cmp::min(self.probe_high, range.end),
- self.sections,
- )?;
- }
- None => return Ok(None),
- },
- Some(mut iter) => {
- if let item @ Some(_) = iter.next() {
- self.iter = Some(iter);
- return Ok(item);
- }
- }
- }
- }
- }
-}
-
-impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R>
-where
- R: gimli::Reader + 'ctx,
-{
- type Item = (u64, u64, Location<'ctx>);
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- match self.next_loc() {
- Err(_) => None,
- Ok(loc) => loc,
- }
- }
-}
-
-#[cfg(feature = "fallible-iterator")]
-impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R>
-where
- R: gimli::Reader + 'ctx,
-{
- type Item = (u64, u64, Location<'ctx>);
- type Error = Error;
-
- #[inline]
- fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
- self.next_loc()
- }
-}
-
-struct LocationRangeUnitIter<'ctx> {
- lines: &'ctx Lines,
- seqs: &'ctx [LineSequence],
- seq_idx: usize,
- row_idx: usize,
- probe_high: u64,
-}
-
-impl<'ctx> LocationRangeUnitIter<'ctx> {
- fn new<R: gimli::Reader>(
- resunit: &'ctx ResUnit<R>,
- sections: &gimli::Dwarf<R>,
- probe_low: u64,
- probe_high: u64,
- ) -> Result<Option<Self>, Error> {
- let lines = resunit.parse_lines(sections)?;
-
- if let Some(lines) = lines {
- // Find index for probe_low.
- let seq_idx = lines.sequences.binary_search_by(|sequence| {
- if probe_low < sequence.start {
- Ordering::Greater
- } else if probe_low >= sequence.end {
- Ordering::Less
- } else {
- Ordering::Equal
- }
- });
- let seq_idx = match seq_idx {
- Ok(x) => x,
- Err(0) => 0, // probe below sequence, but range could overlap
- Err(_) => lines.sequences.len(),
- };
-
- let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) {
- let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low));
- match idx {
- Ok(x) => x,
- Err(0) => 0, // probe below sequence, but range could overlap
- Err(x) => x - 1,
- }
- } else {
- 0
- };
-
- Ok(Some(Self {
- lines,
- seqs: &*lines.sequences,
- seq_idx,
- row_idx,
- probe_high,
- }))
- } else {
- Ok(None)
- }
- }
-}
-
-impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> {
- type Item = (u64, u64, Location<'ctx>);
-
- fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> {
- while let Some(seq) = self.seqs.get(self.seq_idx) {
- if seq.start >= self.probe_high {
- break;
- }
-
- match seq.rows.get(self.row_idx) {
- Some(row) => {
- if row.address >= self.probe_high {
- break;
- }
-
- let file = self
- .lines
- .files
- .get(row.file_index as usize)
- .map(String::as_str);
- let nextaddr = seq
- .rows
- .get(self.row_idx + 1)
- .map(|row| row.address)
- .unwrap_or(seq.end);
-
- let item = (
- row.address,
- nextaddr - row.address,
- Location {
- file,
- line: if row.line != 0 { Some(row.line) } else { None },
- column: if row.column != 0 {
- Some(row.column)
- } else {
- None
- },
- },
- );
- self.row_idx += 1;
-
- return Some(item);
- }
- None => {
- self.seq_idx += 1;
- self.row_idx = 0;
- }
- }
- }
- None
- }
-}
-
-fn path_push(path: &mut String, p: &str) {
- if has_unix_root(p) || has_windows_root(p) {
- *path = p.to_string();
- } else {
- let dir_separator = if has_windows_root(path.as_str()) {
- '\\'
- } else {
- '/'
- };
-
- if !path.is_empty() && !path.ends_with(dir_separator) {
- path.push(dir_separator);
- }
- *path += p;
- }
-}
-
-/// Check if the path in the given string has a unix style root
-fn has_unix_root(p: &str) -> bool {
- p.starts_with('/')
-}
-
-/// Check if the path in the given string has a windows style root
-fn has_windows_root(p: &str) -> bool {
- p.starts_with('\\') || p.get(1..3) == Some(":\\")
-}
-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,
- sections: &gimli::Dwarf<R>,
- unit: &gimli::Unit<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 = sections.ranges(unit, 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) {
- add_range(gimli::Range {
- begin,
- end: begin + size,
- });
- }
- Ok(added_any)
- }
-}
-
-/// An iterator over function frames.
-pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
-where
- R: gimli::Reader;
-
-enum FrameIterState<'ctx, R>
-where
- R: gimli::Reader,
-{
- Empty,
- Location(Option<Location<'ctx>>),
- Frames(FrameIterFrames<'ctx, R>),
-}
-
-struct FrameIterFrames<'ctx, R>
-where
- R: gimli::Reader,
-{
- unit: &'ctx ResUnit<R>,
- sections: &'ctx gimli::Dwarf<R>,
- function: &'ctx Function<R>,
- inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
- next: Option<Location<'ctx>>,
-}
-
-impl<'ctx, R> FrameIter<'ctx, R>
-where
- R: gimli::Reader + 'ctx,
-{
- /// Advances the iterator and returns the next frame.
- pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
- let frames = match &mut self.0 {
- FrameIterState::Empty => return Ok(None),
- FrameIterState::Location(location) => {
- // We can't move out of a mutable reference, so use `take` instead.
- let location = location.take();
- self.0 = FrameIterState::Empty;
- return Ok(Some(Frame {
- dw_die_offset: None,
- function: None,
- location,
- }));
- }
- FrameIterState::Frames(frames) => frames,
- };
-
- let loc = frames.next.take();
- let func = match frames.inlined_functions.next() {
- Some(func) => func,
- None => {
- let frame = Frame {
- dw_die_offset: Some(frames.function.dw_die_offset),
- function: frames.function.name.clone().map(|name| FunctionName {
- name,
- language: frames.unit.lang,
- }),
- location: loc,
- };
- self.0 = FrameIterState::Empty;
- return Ok(Some(frame));
- }
- };
-
- let mut next = Location {
- file: None,
- line: if func.call_line != 0 {
- Some(func.call_line)
- } else {
- None
- },
- column: if func.call_column != 0 {
- Some(func.call_column)
- } else {
- None
- },
- };
- if let Some(call_file) = func.call_file {
- if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
- next.file = lines.files.get(call_file as usize).map(String::as_str);
- }
- }
- frames.next = Some(next);
-
- Ok(Some(Frame {
- dw_die_offset: Some(func.dw_die_offset),
- function: func.name.clone().map(|name| FunctionName {
- name,
- language: frames.unit.lang,
- }),
- location: loc,
- }))
- }
-}
-
-#[cfg(feature = "fallible-iterator")]
-impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
-where
- R: gimli::Reader + 'ctx,
-{
- type Item = Frame<'ctx, R>;
- type Error = Error;
-
- #[inline]
- fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
- self.next()
- }
-}
-
-/// A function frame.
-pub struct Frame<'ctx, R: gimli::Reader> {
- /// The DWARF unit offset corresponding to the DIE of the function.
- pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
- /// The name of the function.
- pub function: Option<FunctionName<R>>,
- /// The source location corresponding to this frame.
- pub location: Option<Location<'ctx>>,
-}
-
-/// A function name.
-pub struct FunctionName<R: gimli::Reader> {
- /// The name of the function.
- pub name: R,
- /// The language of the compilation unit containing this function.
- pub language: Option<gimli::DwLang>,
-}
-
-impl<R: gimli::Reader> FunctionName<R> {
- /// The raw name of this function before demangling.
- pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> {
- self.name.to_string_lossy()
- }
-
- /// The name of this function after demangling (if applicable).
- pub fn demangle(&self) -> Result<Cow<'_, str>, Error> {
- self.raw_name().map(|x| demangle_auto(x, self.language))
- }
-}
-
-/// Demangle a symbol name using the demangling scheme for the given language.
-///
-/// Returns `None` if demangling failed or is not required.
-#[allow(unused_variables)]
-pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
- match language {
- #[cfg(feature = "rustc-demangle")]
- gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name)
- .ok()
- .as_ref()
- .map(|x| format!("{:#}", x)),
- #[cfg(feature = "cpp_demangle")]
- gimli::DW_LANG_C_plus_plus
- | gimli::DW_LANG_C_plus_plus_03
- | gimli::DW_LANG_C_plus_plus_11
- | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name)
- .ok()
- .and_then(|x| x.demangle(&Default::default()).ok()),
- _ => None,
- }
-}
-
-/// Apply 'best effort' demangling of a symbol name.
-///
-/// If `language` is given, then only the demangling scheme for that language
-/// is used.
-///
-/// If `language` is `None`, then heuristics are used to determine how to
-/// demangle the name. Currently, these heuristics are very basic.
-///
-/// If demangling fails or is not required, then `name` is returned unchanged.
-pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> {
- match language {
- Some(language) => demangle(name.as_ref(), language),
- None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
- .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
- }
- .map(Cow::from)
- .unwrap_or(name)
-}
-
-/// A source location.
-pub struct Location<'a> {
- /// The file name.
- pub file: Option<&'a str>,
- /// The line number.
- pub line: Option<u32>,
- /// The column number.
- pub column: Option<u32>,
-}
-
-#[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>>>();
- }
-}