diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/addr2line/src | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/addr2line/src')
-rw-r--r-- | vendor/addr2line/src/builtin_split_dwarf_loader.rs | 164 | ||||
-rw-r--r-- | vendor/addr2line/src/function.rs | 555 | ||||
-rw-r--r-- | vendor/addr2line/src/lazy.rs | 31 | ||||
-rw-r--r-- | vendor/addr2line/src/lib.rs | 1729 |
4 files changed, 0 insertions, 2479 deletions
diff --git a/vendor/addr2line/src/builtin_split_dwarf_loader.rs b/vendor/addr2line/src/builtin_split_dwarf_loader.rs deleted file mode 100644 index 4711931..0000000 --- a/vendor/addr2line/src/builtin_split_dwarf_loader.rs +++ /dev/null @@ -1,164 +0,0 @@ -use alloc::borrow::Cow; -use alloc::sync::Arc; -use std::fs::File; -use std::path::PathBuf; - -use object::Object; - -use crate::{LookupContinuation, LookupResult}; - -#[cfg(unix)] -fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>( - r: &R, -) -> Result<PathBuf, gimli::Error> { - use std::ffi::OsStr; - use std::os::unix::ffi::OsStrExt; - let bytes = r.to_slice()?; - let s = OsStr::from_bytes(&bytes); - Ok(PathBuf::from(s)) -} - -#[cfg(not(unix))] -fn convert_path<R: gimli::Reader<Endian = gimli::RunTimeEndian>>( - r: &R, -) -> Result<PathBuf, gimli::Error> { - let bytes = r.to_slice()?; - let s = std::str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?; - Ok(PathBuf::from(s)) -} - -fn load_section<'data: 'file, 'file, O, R, F>( - id: gimli::SectionId, - file: &'file O, - endian: R::Endian, - loader: &mut F, -) -> Result<R, gimli::Error> -where - O: object::Object<'data, 'file>, - R: gimli::Reader<Endian = gimli::RunTimeEndian>, - F: FnMut(Cow<'data, [u8]>, R::Endian) -> R, -{ - use object::ObjectSection; - - let data = id - .dwo_name() - .and_then(|dwo_name| { - file.section_by_name(dwo_name) - .and_then(|section| section.uncompressed_data().ok()) - }) - .unwrap_or(Cow::Borrowed(&[])); - Ok(loader(data, endian)) -} - -/// A simple builtin split DWARF loader. -pub struct SplitDwarfLoader<R, F> -where - R: gimli::Reader<Endian = gimli::RunTimeEndian>, - F: FnMut(Cow<'_, [u8]>, R::Endian) -> R, -{ - loader: F, - dwarf_package: Option<gimli::DwarfPackage<R>>, -} - -impl<R, F> SplitDwarfLoader<R, F> -where - R: gimli::Reader<Endian = gimli::RunTimeEndian>, - F: FnMut(Cow<'_, [u8]>, R::Endian) -> R, -{ - fn load_dwarf_package(loader: &mut F, path: Option<PathBuf>) -> Option<gimli::DwarfPackage<R>> { - let mut path = path.map(Ok).unwrap_or_else(std::env::current_exe).ok()?; - let dwp_extension = path - .extension() - .map(|previous_extension| { - let mut previous_extension = previous_extension.to_os_string(); - previous_extension.push(".dwp"); - previous_extension - }) - .unwrap_or_else(|| "dwp".into()); - path.set_extension(dwp_extension); - let file = File::open(&path).ok()?; - let map = unsafe { memmap2::Mmap::map(&file).ok()? }; - let dwp = object::File::parse(&*map).ok()?; - - let endian = if dwp.is_little_endian() { - gimli::RunTimeEndian::Little - } else { - gimli::RunTimeEndian::Big - }; - - let empty = loader(Cow::Borrowed(&[]), endian); - gimli::DwarfPackage::load( - |section_id| load_section(section_id, &dwp, endian, loader), - empty, - ) - .ok() - } - - /// Create a new split DWARF loader. - pub fn new(mut loader: F, path: Option<PathBuf>) -> SplitDwarfLoader<R, F> { - let dwarf_package = SplitDwarfLoader::load_dwarf_package(&mut loader, path); - SplitDwarfLoader { - loader, - dwarf_package, - } - } - - /// Run the provided `LookupResult` to completion, loading any necessary - /// split DWARF along the way. - pub fn run<L>(&mut self, mut l: LookupResult<L>) -> L::Output - where - L: LookupContinuation<Buf = R>, - { - loop { - let (load, continuation) = match l { - LookupResult::Output(output) => break output, - LookupResult::Load { load, continuation } => (load, continuation), - }; - - let mut r: Option<Arc<gimli::Dwarf<_>>> = None; - if let Some(dwp) = self.dwarf_package.as_ref() { - if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) { - r = Some(Arc::new(cu)); - } - } - - if r.is_none() { - let mut path = PathBuf::new(); - if let Some(p) = load.comp_dir.as_ref() { - if let Ok(p) = convert_path(p) { - path.push(p); - } - } - - if let Some(p) = load.path.as_ref() { - if let Ok(p) = convert_path(p) { - path.push(p); - } - } - - if let Ok(file) = File::open(&path) { - if let Ok(map) = unsafe { memmap2::Mmap::map(&file) } { - if let Ok(file) = object::File::parse(&*map) { - let endian = if file.is_little_endian() { - gimli::RunTimeEndian::Little - } else { - gimli::RunTimeEndian::Big - }; - - r = gimli::Dwarf::load(|id| { - load_section(id, &file, endian, &mut self.loader) - }) - .ok() - .map(|mut dwo_dwarf| { - dwo_dwarf.make_dwo(&load.parent); - Arc::new(dwo_dwarf) - }); - } - } - } - } - - l = continuation.resume(r); - } - } -} diff --git a/vendor/addr2line/src/function.rs b/vendor/addr2line/src/function.rs deleted file mode 100644 index 09c19e0..0000000 --- a/vendor/addr2line/src/function.rs +++ /dev/null @@ -1,555 +0,0 @@ -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::cmp::Ordering; -use core::iter; - -use crate::lazy::LazyCell; -use crate::maybe_small; -use crate::{Context, DebugFile, Error, RangeAttributes}; - -pub(crate) struct Functions<R: gimli::Reader> { - /// List of all `DW_TAG_subprogram` details in the unit. - pub(crate) functions: Box< - [( - gimli::UnitOffset<R::Offset>, - LazyCell<Result<Function<R>, Error>>, - )], - >, - /// List of `DW_TAG_subprogram` address ranges in the unit. - pub(crate) addresses: Box<[FunctionAddress]>, -} - -/// A single address range for a function. -/// -/// It is possible for a function to have multiple address ranges; this -/// is handled by having multiple `FunctionAddress` entries with the same -/// `function` field. -pub(crate) struct FunctionAddress { - range: gimli::Range, - /// An index into `Functions::functions`. - pub(crate) function: usize, -} - -pub(crate) struct Function<R: gimli::Reader> { - pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, - pub(crate) name: Option<R>, - /// List of all `DW_TAG_inlined_subroutine` details in this function. - inlined_functions: Box<[InlinedFunction<R>]>, - /// List of `DW_TAG_inlined_subroutine` address ranges in this function. - inlined_addresses: Box<[InlinedFunctionAddress]>, -} - -pub(crate) struct InlinedFunctionAddress { - range: gimli::Range, - call_depth: usize, - /// An index into `Function::inlined_functions`. - function: usize, -} - -pub(crate) struct InlinedFunction<R: gimli::Reader> { - pub(crate) dw_die_offset: gimli::UnitOffset<R::Offset>, - pub(crate) name: Option<R>, - pub(crate) call_file: Option<u64>, - pub(crate) call_line: u32, - pub(crate) call_column: u32, -} - -impl<R: gimli::Reader> Functions<R> { - pub(crate) fn parse( - unit: &gimli::Unit<R>, - sections: &gimli::Dwarf<R>, - ) -> Result<Functions<R>, Error> { - let mut functions = Vec::new(); - let mut addresses = Vec::new(); - let mut entries = unit.entries_raw(None)?; - while !entries.is_empty() { - let dw_die_offset = entries.next_offset(); - if let Some(abbrev) = entries.read_abbreviation()? { - if abbrev.tag() == gimli::DW_TAG_subprogram { - let mut ranges = RangeAttributes::default(); - for spec in abbrev.attributes() { - match entries.read_attribute(*spec) { - Ok(ref attr) => { - 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(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(unit, index)?); - } - gimli::AttributeValue::Udata(val) => { - ranges.size = Some(val) - } - _ => {} - }, - gimli::DW_AT_ranges => { - ranges.ranges_offset = - sections.attr_ranges_offset(unit, attr.value())?; - } - _ => {} - }; - } - Err(e) => return Err(e), - } - } - - let function_index = functions.len(); - if ranges.for_each_range(sections, unit, |range| { - addresses.push(FunctionAddress { - range, - function: function_index, - }); - })? { - functions.push((dw_die_offset, LazyCell::new())); - } - } else { - entries.skip_attributes(abbrev.attributes())?; - } - } - } - - // The binary search requires the addresses to be sorted. - // - // It also requires them to be non-overlapping. In practice, overlapping - // function ranges are unlikely, so we don't try to handle that yet. - // - // It's possible for multiple functions to have the same address range if the - // compiler can detect and remove functions with identical code. In that case - // we'll nondeterministically return one of them. - addresses.sort_by_key(|x| x.range.begin); - - Ok(Functions { - functions: functions.into_boxed_slice(), - addresses: addresses.into_boxed_slice(), - }) - } - - pub(crate) fn find_address(&self, probe: u64) -> Option<usize> { - self.addresses - .binary_search_by(|address| { - if probe < address.range.begin { - Ordering::Greater - } else if probe >= address.range.end { - Ordering::Less - } else { - Ordering::Equal - } - }) - .ok() - } - - pub(crate) fn parse_inlined_functions( - &self, - file: DebugFile, - unit: &gimli::Unit<R>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - ) -> Result<(), Error> { - for function in &*self.functions { - function - .1 - .borrow_with(|| Function::parse(function.0, file, unit, ctx, sections)) - .as_ref() - .map_err(Error::clone)?; - } - Ok(()) - } -} - -impl<R: gimli::Reader> Function<R> { - pub(crate) fn parse( - dw_die_offset: gimli::UnitOffset<R::Offset>, - file: DebugFile, - unit: &gimli::Unit<R>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - ) -> Result<Self, Error> { - let mut entries = unit.entries_raw(Some(dw_die_offset))?; - let depth = entries.next_depth(); - let abbrev = entries.read_abbreviation()?.unwrap(); - debug_assert_eq!(abbrev.tag(), gimli::DW_TAG_subprogram); - - let mut name = None; - for spec in abbrev.attributes() { - match entries.read_attribute(*spec) { - Ok(ref attr) => { - match attr.name() { - gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { - if let Ok(val) = sections.attr_string(unit, attr.value()) { - name = Some(val); - } - } - gimli::DW_AT_name => { - if name.is_none() { - name = sections.attr_string(unit, attr.value()).ok(); - } - } - gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { - if name.is_none() { - name = name_attr(attr.value(), file, unit, ctx, sections, 16)?; - } - } - _ => {} - }; - } - Err(e) => return Err(e), - } - } - - let mut inlined_functions = Vec::new(); - let mut inlined_addresses = Vec::new(); - Function::parse_children( - &mut entries, - depth, - file, - unit, - ctx, - sections, - &mut inlined_functions, - &mut inlined_addresses, - 0, - )?; - - // Sort ranges in "breadth-first traversal order", i.e. first by call_depth - // and then by range.begin. This allows finding the range containing an - // address at a certain depth using binary search. - // Note: Using DFS order, i.e. ordering by range.begin first and then by - // call_depth, would not work! Consider the two examples - // "[0..10 at depth 0], [0..2 at depth 1], [6..8 at depth 1]" and - // "[0..5 at depth 0], [0..2 at depth 1], [5..10 at depth 0], [6..8 at depth 1]". - // In this example, if you want to look up address 7 at depth 0, and you - // encounter [0..2 at depth 1], are you before or after the target range? - // You don't know. - inlined_addresses.sort_by(|r1, r2| { - if r1.call_depth < r2.call_depth { - Ordering::Less - } else if r1.call_depth > r2.call_depth { - Ordering::Greater - } else if r1.range.begin < r2.range.begin { - Ordering::Less - } else if r1.range.begin > r2.range.begin { - Ordering::Greater - } else { - Ordering::Equal - } - }); - - Ok(Function { - dw_die_offset, - name, - inlined_functions: inlined_functions.into_boxed_slice(), - inlined_addresses: inlined_addresses.into_boxed_slice(), - }) - } - - fn parse_children( - entries: &mut gimli::EntriesRaw<'_, '_, R>, - depth: isize, - file: DebugFile, - unit: &gimli::Unit<R>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - inlined_functions: &mut Vec<InlinedFunction<R>>, - inlined_addresses: &mut Vec<InlinedFunctionAddress>, - inlined_depth: usize, - ) -> Result<(), Error> { - loop { - let dw_die_offset = entries.next_offset(); - let next_depth = entries.next_depth(); - if next_depth <= depth { - return Ok(()); - } - if let Some(abbrev) = entries.read_abbreviation()? { - match abbrev.tag() { - gimli::DW_TAG_subprogram => { - Function::skip(entries, abbrev, next_depth)?; - } - gimli::DW_TAG_inlined_subroutine => { - InlinedFunction::parse( - dw_die_offset, - entries, - abbrev, - next_depth, - file, - unit, - ctx, - sections, - inlined_functions, - inlined_addresses, - inlined_depth, - )?; - } - _ => { - entries.skip_attributes(abbrev.attributes())?; - } - } - } - } - } - - fn skip( - entries: &mut gimli::EntriesRaw<'_, '_, R>, - abbrev: &gimli::Abbreviation, - depth: isize, - ) -> Result<(), Error> { - // TODO: use DW_AT_sibling - entries.skip_attributes(abbrev.attributes())?; - while entries.next_depth() > depth { - if let Some(abbrev) = entries.read_abbreviation()? { - entries.skip_attributes(abbrev.attributes())?; - } - } - Ok(()) - } - - /// Build the list of inlined functions that contain `probe`. - pub(crate) fn find_inlined_functions( - &self, - probe: u64, - ) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>> { - // `inlined_functions` is ordered from outside to inside. - let mut inlined_functions = maybe_small::Vec::new(); - let mut inlined_addresses = &self.inlined_addresses[..]; - loop { - let current_depth = inlined_functions.len(); - // Look up (probe, current_depth) in inline_ranges. - // `inlined_addresses` is sorted in "breadth-first traversal order", i.e. - // by `call_depth` first, and then by `range.begin`. See the comment at - // the sort call for more information about why. - let search = inlined_addresses.binary_search_by(|range| { - if range.call_depth > current_depth { - Ordering::Greater - } else if range.call_depth < current_depth { - Ordering::Less - } else if range.range.begin > probe { - Ordering::Greater - } else if range.range.end <= probe { - Ordering::Less - } else { - Ordering::Equal - } - }); - if let Ok(index) = search { - let function_index = inlined_addresses[index].function; - inlined_functions.push(&self.inlined_functions[function_index]); - inlined_addresses = &inlined_addresses[index + 1..]; - } else { - break; - } - } - inlined_functions.into_iter().rev() - } -} - -impl<R: gimli::Reader> InlinedFunction<R> { - fn parse( - dw_die_offset: gimli::UnitOffset<R::Offset>, - entries: &mut gimli::EntriesRaw<'_, '_, R>, - abbrev: &gimli::Abbreviation, - depth: isize, - file: DebugFile, - unit: &gimli::Unit<R>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - inlined_functions: &mut Vec<InlinedFunction<R>>, - inlined_addresses: &mut Vec<InlinedFunctionAddress>, - inlined_depth: usize, - ) -> Result<(), Error> { - let mut ranges = RangeAttributes::default(); - let mut name = None; - let mut call_file = None; - let mut call_line = 0; - let mut call_column = 0; - for spec in abbrev.attributes() { - match entries.read_attribute(*spec) { - Ok(ref attr) => 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(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(unit, index)?); - } - gimli::AttributeValue::Udata(val) => ranges.size = Some(val), - _ => {} - }, - gimli::DW_AT_ranges => { - ranges.ranges_offset = sections.attr_ranges_offset(unit, attr.value())?; - } - gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { - if let Ok(val) = sections.attr_string(unit, attr.value()) { - name = Some(val); - } - } - gimli::DW_AT_name => { - if name.is_none() { - name = sections.attr_string(unit, attr.value()).ok(); - } - } - gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { - if name.is_none() { - name = name_attr(attr.value(), file, unit, ctx, sections, 16)?; - } - } - gimli::DW_AT_call_file => { - // There is a spec issue [1] with how DW_AT_call_file is specified in DWARF 5. - // Before, a file index of 0 would indicate no source file, however in - // DWARF 5 this could be a valid index into the file table. - // - // Implementations such as LLVM generates a file index of 0 when DWARF 5 is - // used. - // - // Thus, if we see a version of 5 or later, treat a file index of 0 as such. - // [1]: http://wiki.dwarfstd.org/index.php?title=DWARF5_Line_Table_File_Numbers - if let gimli::AttributeValue::FileIndex(fi) = attr.value() { - if fi > 0 || unit.header.version() >= 5 { - call_file = Some(fi); - } - } - } - gimli::DW_AT_call_line => { - call_line = attr.udata_value().unwrap_or(0) as u32; - } - gimli::DW_AT_call_column => { - call_column = attr.udata_value().unwrap_or(0) as u32; - } - _ => {} - }, - Err(e) => return Err(e), - } - } - - let function_index = inlined_functions.len(); - inlined_functions.push(InlinedFunction { - dw_die_offset, - name, - call_file, - call_line, - call_column, - }); - - ranges.for_each_range(sections, unit, |range| { - inlined_addresses.push(InlinedFunctionAddress { - range, - call_depth: inlined_depth, - function: function_index, - }); - })?; - - Function::parse_children( - entries, - depth, - file, - unit, - ctx, - sections, - inlined_functions, - inlined_addresses, - inlined_depth + 1, - ) - } -} - -fn name_attr<R>( - attr: gimli::AttributeValue<R>, - mut file: DebugFile, - unit: &gimli::Unit<R>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - recursion_limit: usize, -) -> Result<Option<R>, Error> -where - R: gimli::Reader, -{ - if recursion_limit == 0 { - return Ok(None); - } - - match attr { - gimli::AttributeValue::UnitRef(offset) => { - name_entry(file, unit, offset, ctx, sections, recursion_limit) - } - gimli::AttributeValue::DebugInfoRef(dr) => { - let (unit, offset) = ctx.find_unit(dr, file)?; - name_entry(file, unit, offset, ctx, sections, recursion_limit) - } - gimli::AttributeValue::DebugInfoRefSup(dr) => { - if let Some(sup_sections) = sections.sup.as_ref() { - file = DebugFile::Supplementary; - let (unit, offset) = ctx.find_unit(dr, file)?; - name_entry(file, unit, offset, ctx, sup_sections, recursion_limit) - } else { - Ok(None) - } - } - _ => Ok(None), - } -} - -fn name_entry<R>( - file: DebugFile, - unit: &gimli::Unit<R>, - offset: gimli::UnitOffset<R::Offset>, - ctx: &Context<R>, - sections: &gimli::Dwarf<R>, - recursion_limit: usize, -) -> Result<Option<R>, Error> -where - R: gimli::Reader, -{ - let mut entries = unit.entries_raw(Some(offset))?; - let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { - abbrev - } else { - return Err(gimli::Error::NoEntryAtGivenOffset); - }; - - let mut name = None; - let mut next = None; - for spec in abbrev.attributes() { - match entries.read_attribute(*spec) { - Ok(ref attr) => match attr.name() { - gimli::DW_AT_linkage_name | gimli::DW_AT_MIPS_linkage_name => { - if let Ok(val) = sections.attr_string(unit, attr.value()) { - return Ok(Some(val)); - } - } - gimli::DW_AT_name => { - if let Ok(val) = sections.attr_string(unit, attr.value()) { - name = Some(val); - } - } - gimli::DW_AT_abstract_origin | gimli::DW_AT_specification => { - next = Some(attr.value()); - } - _ => {} - }, - Err(e) => return Err(e), - } - } - - if name.is_some() { - return Ok(name); - } - - if let Some(next) = next { - return name_attr(next, file, unit, ctx, sections, recursion_limit - 1); - } - - Ok(None) -} diff --git a/vendor/addr2line/src/lazy.rs b/vendor/addr2line/src/lazy.rs deleted file mode 100644 index 2df2ed6..0000000 --- a/vendor/addr2line/src/lazy.rs +++ /dev/null @@ -1,31 +0,0 @@ -use core::cell::UnsafeCell; - -pub struct LazyCell<T> { - contents: UnsafeCell<Option<T>>, -} -impl<T> LazyCell<T> { - pub fn new() -> LazyCell<T> { - LazyCell { - contents: UnsafeCell::new(None), - } - } - - pub fn borrow(&self) -> Option<&T> { - unsafe { &*self.contents.get() }.as_ref() - } - - pub fn borrow_with(&self, closure: impl FnOnce() -> T) -> &T { - // First check if we're already initialized... - let ptr = self.contents.get(); - if let Some(val) = unsafe { &*ptr } { - return val; - } - // Note that while we're executing `closure` our `borrow_with` may - // be called recursively. This means we need to check again after - // the closure has executed. For that we use the `get_or_insert` - // method which will only perform mutation if we aren't already - // `Some`. - let val = closure(); - unsafe { (*ptr).get_or_insert(val) } - } -} 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(§ions)?; - 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>>>(); - } -} |