1 //! This crate provides a cross-platform library and binary for translating addresses into
2 //! function names, file names and line numbers. Given an address in an executable or an
3 //! offset in a section of a relocatable object, it uses the debugging information to
4 //! figure out which file name and line number are associated with it.
6 //! When used as a library, files must first be loaded using the
7 //! [`object`](https://github.com/gimli-rs/object) crate.
8 //! A context can then be created with [`Context::new`](./struct.Context.html#method.new).
9 //! The context caches some of the parsed information so that multiple lookups are
11 //! Location information is obtained with
12 //! [`Context::find_location`](./struct.Context.html#method.find_location) or
13 //! [`Context::find_location_range`](./struct.Context.html#method.find_location_range).
14 //! Function information is obtained with
15 //! [`Context::find_frames`](./struct.Context.html#method.find_frames), which returns
16 //! a frame for each inline function. Each frame contains both name and location.
18 //! The crate has an example CLI wrapper around the library which provides some of
19 //! the functionality of the `addr2line` command line tool distributed with [GNU
20 //! binutils](https://www.gnu.org/software/binutils/).
22 //! Currently this library only provides information from the DWARF debugging information,
23 //! which is parsed using [`gimli`](https://github.com/gimli-rs/gimli). The example CLI
24 //! wrapper also uses symbol table information provided by the `object` crate.
25 #![deny(missing_docs)]
28 #[cfg(feature = "std")]
31 #[allow(unused_imports)]
35 #[cfg(feature = "fallible-iterator")]
36 pub extern crate fallible_iterator
;
37 pub extern crate gimli
;
38 #[cfg(feature = "object")]
39 pub extern crate object
;
41 use alloc
::borrow
::Cow
;
42 use alloc
::boxed
::Box
;
43 #[cfg(feature = "object")]
45 use alloc
::string
::{String, ToString}
;
49 use core
::cmp
::{self, Ordering}
;
51 use core
::marker
::PhantomData
;
53 use core
::num
::NonZeroU64
;
54 use core
::ops
::ControlFlow
;
57 use crate::function
::{Function, Functions, InlinedFunction}
;
58 use crate::lazy
::LazyCell
;
60 #[cfg(feature = "smallvec")]
62 pub type Vec
<T
> = smallvec
::SmallVec
<[T
; 16]>;
63 pub type IntoIter
<T
> = smallvec
::IntoIter
<[T
; 16]>;
65 #[cfg(not(feature = "smallvec"))]
67 pub type Vec
<T
> = alloc
::vec
::Vec
<T
>;
68 pub type IntoIter
<T
> = alloc
::vec
::IntoIter
<T
>;
71 #[cfg(all(feature = "std", feature = "object", feature = "memmap2"))]
72 /// A simple builtin split DWARF loader.
73 pub mod builtin_split_dwarf_loader
;
77 type Error
= gimli
::Error
;
79 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
86 /// Operations that consult debug information may require additional files
87 /// to be loaded if split DWARF is being used. This enum returns the result
88 /// of the operation in the `Break` variant, or information about the split
89 /// DWARF that is required and a continuation to invoke once it is available
90 /// in the `Continue` variant.
92 /// This enum is intended to be used in a loop like so:
94 /// # use addr2line::*;
95 /// # use std::sync::Arc;
96 /// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
97 /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
98 /// const ADDRESS: u64 = 0xdeadbeef;
99 /// let mut r = ctx.find_frames(ADDRESS);
100 /// let result = loop {
102 /// LookupResult::Output(result) => break result,
103 /// LookupResult::Load { load, continuation } => {
104 /// let dwo = do_split_dwarf_load(load);
105 /// r = continuation.resume(dwo);
110 pub enum LookupResult
<L
: LookupContinuation
> {
111 /// The lookup requires split DWARF data to be loaded.
113 /// The information needed to find the split DWARF data.
114 load
: SplitDwarfLoad
<<L
as LookupContinuation
>::Buf
>,
115 /// The continuation to resume with the loaded split DWARF data.
118 /// The lookup has completed and produced an output.
119 Output(<L
as LookupContinuation
>::Output
),
122 /// This trait represents a partially complete operation that can be resumed
123 /// once a load of needed split DWARF data is completed or abandoned by the
125 pub trait LookupContinuation
: Sized
{
126 /// The final output of this operation.
128 /// The type of reader used.
129 type Buf
: gimli
::Reader
;
131 /// Resumes the operation with the provided data.
133 /// After the caller loads the split DWARF data required, call this
134 /// method to resume the operation. The return value of this method
135 /// indicates if the computation has completed or if further data is
138 /// If the additional data cannot be located, or the caller does not
139 /// support split DWARF, `resume(None)` can be used to continue the
140 /// operation with the data that is available.
141 fn resume(self, input
: Option
<Arc
<gimli
::Dwarf
<Self::Buf
>>>) -> LookupResult
<Self>;
144 impl<L
: LookupContinuation
> LookupResult
<L
> {
145 /// Callers that do not handle split DWARF can call `skip_all_loads`
146 /// to fast-forward to the end result. This result is produced with
147 /// the data that is available and may be less accurate than the
148 /// the results that would be produced if the caller did properly
149 /// support split DWARF.
150 pub fn skip_all_loads(mut self) -> L
::Output
{
153 LookupResult
::Output(t
) => return t
,
154 LookupResult
::Load { continuation, .. }
=> continuation
.resume(None
),
159 fn map
<T
, F
: FnOnce(L
::Output
) -> T
>(self, f
: F
) -> LookupResult
<MappedLookup
<T
, L
, F
>> {
161 LookupResult
::Output(t
) => LookupResult
::Output(f(t
)),
162 LookupResult
::Load { load, continuation }
=> LookupResult
::Load
{
164 continuation
: MappedLookup
{
165 original
: continuation
,
172 fn unwrap(self) -> L
::Output
{
174 LookupResult
::Output(t
) => t
,
175 LookupResult
::Load { .. }
=> unreachable
!("Internal API misuse"),
180 /// The state necessary to perform address to line translation.
182 /// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
183 /// when performing lookups for many addresses in the same executable.
184 pub struct Context
<R
: gimli
::Reader
> {
185 sections
: Arc
<gimli
::Dwarf
<R
>>,
186 unit_ranges
: Vec
<UnitRange
>,
187 units
: Vec
<ResUnit
<R
>>,
188 sup_units
: Vec
<SupUnit
<R
>>,
191 /// The type of `Context` that supports the `new` method.
192 #[cfg(feature = "std-object")]
193 pub type ObjectContext
= Context
<gimli
::EndianRcSlice
<gimli
::RunTimeEndian
>>;
195 #[cfg(feature = "std-object")]
196 impl Context
<gimli
::EndianRcSlice
<gimli
::RunTimeEndian
>> {
197 /// Construct a new `Context`.
199 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
200 /// This means it is not thread safe, has no lifetime constraints (since it copies
201 /// the input data), and works for any endianity.
203 /// Performance sensitive applications may want to use `Context::from_dwarf`
204 /// with a more specialised `gimli::Reader` implementation.
206 pub fn new
<'data
: 'file
, 'file
, O
: object
::Object
<'data
, 'file
>>(
208 ) -> Result
<Self, Error
> {
209 Self::new_with_sup(file
, None
)
212 /// Construct a new `Context`.
214 /// Optionally also use a supplementary object file.
216 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
217 /// This means it is not thread safe, has no lifetime constraints (since it copies
218 /// the input data), and works for any endianity.
220 /// Performance sensitive applications may want to use `Context::from_dwarf`
221 /// with a more specialised `gimli::Reader` implementation.
222 pub fn new_with_sup
<'data
: 'file
, 'file
, O
: object
::Object
<'data
, 'file
>>(
224 sup_file
: Option
<&'file O
>,
225 ) -> Result
<Self, Error
> {
226 let endian
= if file
.is_little_endian() {
227 gimli
::RunTimeEndian
::Little
229 gimli
::RunTimeEndian
::Big
232 fn load_section
<'data
: 'file
, 'file
, O
, Endian
>(
233 id
: gimli
::SectionId
,
236 ) -> Result
<gimli
::EndianRcSlice
<Endian
>, Error
>
238 O
: object
::Object
<'data
, 'file
>,
239 Endian
: gimli
::Endianity
,
241 use object
::ObjectSection
;
244 .section_by_name(id
.name())
245 .and_then(|section
| section
.uncompressed_data().ok())
246 .unwrap_or(Cow
::Borrowed(&[]));
247 Ok(gimli
::EndianRcSlice
::new(Rc
::from(&*data
), endian
))
250 let mut dwarf
= gimli
::Dwarf
::load(|id
| load_section(id
, file
, endian
))?
;
251 if let Some(sup_file
) = sup_file
{
252 dwarf
.load_sup(|id
| load_section(id
, sup_file
, endian
))?
;
254 Context
::from_dwarf(dwarf
)
258 impl<R
: gimli
::Reader
> Context
<R
> {
259 /// Construct a new `Context` from DWARF sections.
261 /// This method does not support using a supplementary object file.
262 pub fn from_sections(
263 debug_abbrev
: gimli
::DebugAbbrev
<R
>,
264 debug_addr
: gimli
::DebugAddr
<R
>,
265 debug_aranges
: gimli
::DebugAranges
<R
>,
266 debug_info
: gimli
::DebugInfo
<R
>,
267 debug_line
: gimli
::DebugLine
<R
>,
268 debug_line_str
: gimli
::DebugLineStr
<R
>,
269 debug_ranges
: gimli
::DebugRanges
<R
>,
270 debug_rnglists
: gimli
::DebugRngLists
<R
>,
271 debug_str
: gimli
::DebugStr
<R
>,
272 debug_str_offsets
: gimli
::DebugStrOffsets
<R
>,
274 ) -> Result
<Self, Error
> {
275 Self::from_dwarf(gimli
::Dwarf
{
284 debug_types
: default_section
.clone().into(),
285 locations
: gimli
::LocationLists
::new(
286 default_section
.clone().into(),
287 default_section
.into(),
289 ranges
: gimli
::RangeLists
::new(debug_ranges
, debug_rnglists
),
290 file_type
: gimli
::DwarfFileType
::Main
,
292 abbreviations_cache
: gimli
::AbbreviationsCache
::new(),
296 /// Construct a new `Context` from an existing [`gimli::Dwarf`] object.
298 pub fn from_dwarf(sections
: gimli
::Dwarf
<R
>) -> Result
<Context
<R
>, Error
> {
299 let sections
= Arc
::new(sections
);
300 let (unit_ranges
, units
) = Context
::parse_units(§ions
)?
;
301 let sup_units
= if let Some(sup
) = sections
.sup
.as_ref() {
302 Context
::parse_sup(sup
)?
314 /// Finds the CUs for the function address given.
316 /// There might be multiple CUs whose range contains this address.
317 /// Weak symbols have shown up in the wild which cause this to happen
318 /// but otherwise this can happen if the CU has non-contiguous functions
319 /// but only reports a single range.
321 /// Consequently we return an iterator for all CUs which may contain the
322 /// address, and the caller must check if there is actually a function or
323 /// location in the CU for that address.
324 fn find_units(&self, probe
: u64) -> impl Iterator
<Item
= &ResUnit
<R
>> {
325 self.find_units_range(probe
, probe
+ 1)
326 .map(|(unit
, _range
)| unit
)
329 /// Finds the CUs covering the range of addresses given.
331 /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple
332 /// ranges for the same unit.
338 ) -> impl Iterator
<Item
= (&ResUnit
<R
>, &gimli
::Range
)> {
339 // First up find the position in the array which could have our function
343 .binary_search_by_key(&probe_high
, |i
| i
.range
.begin
)
345 // Although unlikely, we could find an exact match.
347 // No exact match was found, but this probe would fit at slot `i`.
348 // This means that slot `i` is bigger than `probe`, along with all
349 // indices greater than `i`, so we need to search all previous
354 // Once we have our index we iterate backwards from that position
355 // looking for a matching CU.
356 self.unit_ranges
[..pos
]
359 .take_while(move |i
| {
360 // We know that this CU's start is beneath the probe already because
361 // of our sorted array.
362 debug_assert
!(i
.range
.begin
<= probe_high
);
364 // Each entry keeps track of the maximum end address seen so far,
365 // starting from the beginning of the array of unit ranges. We're
366 // iterating in reverse so if our probe is beyond the maximum range
367 // of this entry, then it's guaranteed to not fit in any prior
368 // entries, so we break out.
369 probe_low
< i
.max_end
371 .filter_map(move |i
| {
372 // If this CU doesn't actually contain this address, move to the
374 if probe_low
>= i
.range
.end
|| probe_high
<= i
.range
.begin
{
377 Some((&self.units
[i
.unit_id
], &i
.range
))
381 /// Find the DWARF unit corresponding to the given virtual memory address.
382 pub fn find_dwarf_and_unit(
386 impl LookupContinuation
<Output
= Option
<(&gimli
::Dwarf
<R
>, &gimli
::Unit
<R
>)>, Buf
= R
>,
388 let mut units_iter
= self.find_units(probe
);
389 if let Some(unit
) = units_iter
.next() {
390 return LoopingLookup
::new_lookup(
391 unit
.find_function_or_location(probe
, self),
393 ControlFlow
::Break(match r
{
394 Ok((Some(_
), _
)) | Ok((_
, Some(_
))) => {
395 let (_file
, sections
, unit
) = unit
396 .dwarf_and_unit_dwo(self)
397 // We've already been through both error cases here to get to this point.
400 Some((sections
, unit
))
402 _
=> match units_iter
.next() {
404 return ControlFlow
::Continue(
405 next_unit
.find_function_or_location(probe
, self),
415 LoopingLookup
::new_complete(None
)
418 /// Find the source file and line corresponding to the given virtual memory address.
419 pub fn find_location(&self, probe
: u64) -> Result
<Option
<Location
<'_
>>, Error
> {
420 for unit
in self.find_units(probe
) {
421 if let Some(location
) = unit
.find_location(probe
, &self.sections
)?
{
422 return Ok(Some(location
));
428 /// Return source file and lines for a range of addresses. For each location it also
429 /// returns the address and size of the range of the underlying instructions.
430 pub fn find_location_range(
434 ) -> Result
<LocationRangeIter
<'_
, R
>, Error
> {
435 LocationRangeIter
::new(self, probe_low
, probe_high
)
438 /// Return an iterator for the function frames corresponding to the given virtual
441 /// If the probe address is not for an inline function then only one frame is
444 /// If the probe address is for an inline function then the first frame corresponds
445 /// to the innermost inline function. Subsequent frames contain the caller and call
446 /// location, until an non-inline caller is reached.
450 ) -> LookupResult
<impl LookupContinuation
<Output
= Result
<FrameIter
<'_
, R
>, Error
>, Buf
= R
>>
452 let mut units_iter
= self.find_units(probe
);
453 if let Some(unit
) = units_iter
.next() {
454 LoopingLookup
::new_lookup(unit
.find_function_or_location(probe
, self), move |r
| {
455 ControlFlow
::Break(match r
{
457 Ok((Some(function
), location
)) => {
458 let inlined_functions
= function
.find_inlined_functions(probe
);
459 Ok(FrameIter(FrameIterState
::Frames(FrameIterFrames
{
461 sections
: &self.sections
,
467 Ok((None
, Some(location
))) => {
468 Ok(FrameIter(FrameIterState
::Location(Some(location
))))
470 Ok((None
, None
)) => match units_iter
.next() {
472 return ControlFlow
::Continue(
473 next_unit
.find_function_or_location(probe
, self),
476 None
=> Ok(FrameIter(FrameIterState
::Empty
)),
481 LoopingLookup
::new_complete(Ok(FrameIter(FrameIterState
::Empty
)))
485 /// Preload units for `probe`.
487 /// The iterator returns pairs of `SplitDwarfLoad`s containing the
488 /// information needed to locate and load split DWARF for `probe` and
489 /// a matching callback to invoke once that data is available.
491 /// If this method is called, and all of the returned closures are invoked,
492 /// addr2line guarantees that any future API call for the address `probe`
493 /// will not require the loading of any split DWARF.
496 /// # use addr2line::*;
497 /// # use std::sync::Arc;
498 /// # let ctx: Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> = todo!();
499 /// # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianRcSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianRcSlice<gimli::RunTimeEndian>>>> { None };
500 /// const ADDRESS: u64 = 0xdeadbeef;
501 /// ctx.preload_units(ADDRESS).for_each(|(load, callback)| {
502 /// let dwo = do_split_dwarf_load(load);
506 /// let frames_iter = match ctx.find_frames(ADDRESS) {
507 /// LookupResult::Output(result) => result,
508 /// LookupResult::Load { .. } => unreachable!("addr2line promised we wouldn't get here"),
513 pub fn preload_units(
519 impl FnOnce(Option
<Arc
<gimli
::Dwarf
<R
>>>) -> Result
<(), gimli
::Error
> + '_
,
522 self.find_units(probe
)
523 .filter_map(move |unit
| match unit
.dwarf_and_unit_dwo(self) {
524 LookupResult
::Output(_
) => None
,
525 LookupResult
::Load { load, continuation }
=> Some((load
, |result
| {
526 continuation
.resume(result
).unwrap().map(|_
| ())
531 /// Initialize all line data structures. This is used for benchmarks.
533 pub fn parse_lines(&self) -> Result
<(), Error
> {
534 for unit
in &self.units
{
535 unit
.parse_lines(&self.sections
)?
;
540 /// Initialize all function data structures. This is used for benchmarks.
542 pub fn parse_functions(&self) -> Result
<(), Error
> {
543 for unit
in &self.units
{
544 unit
.parse_functions(self).skip_all_loads()?
;
549 /// Initialize all inlined function data structures. This is used for benchmarks.
551 pub fn parse_inlined_functions(&self) -> Result
<(), Error
> {
552 for unit
in &self.units
{
553 unit
.parse_inlined_functions(self).skip_all_loads()?
;
565 struct ResUnit
<R
: gimli
::Reader
> {
566 offset
: gimli
::DebugInfoOffset
<R
::Offset
>,
567 dw_unit
: gimli
::Unit
<R
>,
568 lang
: Option
<gimli
::DwLang
>,
569 lines
: LazyCell
<Result
<Lines
, Error
>>,
570 funcs
: LazyCell
<Result
<Functions
<R
>, Error
>>,
571 dwo
: LazyCell
<Result
<Option
<Box
<(Arc
<gimli
::Dwarf
<R
>>, gimli
::Unit
<R
>)>>, Error
>>,
574 struct SupUnit
<R
: gimli
::Reader
> {
575 offset
: gimli
::DebugInfoOffset
<R
::Offset
>,
576 dw_unit
: gimli
::Unit
<R
>,
579 impl<R
: gimli
::Reader
> Context
<R
> {
580 fn parse_units(sections
: &gimli
::Dwarf
<R
>) -> Result
<(Vec
<UnitRange
>, Vec
<ResUnit
<R
>>), Error
> {
581 // Find all the references to compilation units in .debug_aranges.
582 // Note that we always also iterate through all of .debug_info to
583 // find compilation units, because .debug_aranges may be missing some.
584 let mut aranges
= Vec
::new();
585 let mut headers
= sections
.debug_aranges
.headers();
586 while let Some(header
) = headers
.next()?
{
587 aranges
.push((header
.debug_info_offset(), header
.offset()));
589 aranges
.sort_by_key(|i
| i
.0);
591 let mut unit_ranges
= Vec
::new();
592 let mut res_units
= Vec
::new();
593 let mut units
= sections
.units();
594 while let Some(header
) = units
.next()?
{
595 let unit_id
= res_units
.len();
596 let offset
= match header
.offset().as_debug_info_offset() {
597 Some(offset
) => offset
,
600 // We mainly want compile units, but we may need to follow references to entries
601 // within other units for function names. We don't need anything from type units.
602 match header
.type_() {
603 gimli
::UnitType
::Type { .. }
| gimli
::UnitType
::SplitType { .. }
=> continue,
606 let dw_unit
= match sections
.unit(header
) {
607 Ok(dw_unit
) => dw_unit
,
612 let mut have_unit_range
= false;
614 let mut entries
= dw_unit
.entries_raw(None
)?
;
616 let abbrev
= match entries
.read_abbreviation()?
{
617 Some(abbrev
) => abbrev
,
621 let mut ranges
= RangeAttributes
::default();
622 for spec
in abbrev
.attributes() {
623 let attr
= entries
.read_attribute(*spec
)?
;
625 gimli
::DW_AT_low_pc
=> match attr
.value() {
626 gimli
::AttributeValue
::Addr(val
) => ranges
.low_pc
= Some(val
),
627 gimli
::AttributeValue
::DebugAddrIndex(index
) => {
628 ranges
.low_pc
= Some(sections
.address(&dw_unit
, index
)?
);
632 gimli
::DW_AT_high_pc
=> match attr
.value() {
633 gimli
::AttributeValue
::Addr(val
) => ranges
.high_pc
= Some(val
),
634 gimli
::AttributeValue
::DebugAddrIndex(index
) => {
635 ranges
.high_pc
= Some(sections
.address(&dw_unit
, index
)?
);
637 gimli
::AttributeValue
::Udata(val
) => ranges
.size
= Some(val
),
640 gimli
::DW_AT_ranges
=> {
641 ranges
.ranges_offset
=
642 sections
.attr_ranges_offset(&dw_unit
, attr
.value())?
;
644 gimli
::DW_AT_language
=> {
645 if let gimli
::AttributeValue
::Language(val
) = attr
.value() {
653 // Find the address ranges for the CU, using in order of preference:
656 // - DW_AT_low_pc/DW_AT_high_pc
658 // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice,
659 // but the feeling is that DW_AT_ranges is more likely to be reliable or complete
662 // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because
663 // it has been observed on macOS that DW_AT_ranges was not emitted even for
664 // discontiguous CUs.
665 let i
= match ranges
.ranges_offset
{
667 None
=> aranges
.binary_search_by_key(&offset
, |x
| x
.0).ok(),
669 if let Some(mut i
) = i
{
670 // There should be only one set per CU, but in practice multiple
671 // sets have been observed. This is probably a compiler bug, but
672 // either way we need to handle it.
673 while i
> 0 && aranges
[i
- 1].0 == offset
{
676 for (_
, aranges_offset
) in aranges
[i
..].iter().take_while(|x
| x
.0 == offset
) {
677 let aranges_header
= sections
.debug_aranges
.header(*aranges_offset
)?
;
678 let mut aranges
= aranges_header
.entries();
679 while let Some(arange
) = aranges
.next()?
{
680 if arange
.length() != 0 {
681 unit_ranges
.push(UnitRange
{
682 range
: arange
.range(),
686 have_unit_range
= true;
691 have_unit_range
|= ranges
.for_each_range(sections
, &dw_unit
, |range
| {
692 unit_ranges
.push(UnitRange
{
701 let lines
= LazyCell
::new();
702 if !have_unit_range
{
703 // The unit did not declare any ranges.
704 // Try to get some ranges from the line program sequences.
705 if let Some(ref ilnp
) = dw_unit
.line_program
{
706 if let Ok(lines
) = lines
707 .borrow_with(|| Lines
::parse(&dw_unit
, ilnp
.clone(), sections
))
710 for sequence
in lines
.sequences
.iter() {
711 unit_ranges
.push(UnitRange
{
712 range
: gimli
::Range
{
713 begin
: sequence
.start
,
724 res_units
.push(ResUnit
{
729 funcs
: LazyCell
::new(),
730 dwo
: LazyCell
::new(),
734 // Sort this for faster lookup in `find_unit_and_address` below.
735 unit_ranges
.sort_by_key(|i
| i
.range
.begin
);
737 // Calculate the `max_end` field now that we've determined the order of
740 for i
in unit_ranges
.iter_mut() {
741 max
= max
.max(i
.range
.end
);
745 Ok((unit_ranges
, res_units
))
748 fn parse_sup(sections
: &gimli
::Dwarf
<R
>) -> Result
<Vec
<SupUnit
<R
>>, Error
> {
749 let mut sup_units
= Vec
::new();
750 let mut units
= sections
.units();
751 while let Some(header
) = units
.next()?
{
752 let offset
= match header
.offset().as_debug_info_offset() {
753 Some(offset
) => offset
,
756 let dw_unit
= match sections
.unit(header
) {
757 Ok(dw_unit
) => dw_unit
,
760 sup_units
.push(SupUnit { dw_unit, offset }
);
765 // Find the unit containing the given offset, and convert the offset into a unit offset.
768 offset
: gimli
::DebugInfoOffset
<R
::Offset
>,
770 ) -> Result
<(&gimli
::Unit
<R
>, gimli
::UnitOffset
<R
::Offset
>), Error
> {
771 let unit
= match file
{
772 DebugFile
::Primary
=> {
775 .binary_search_by_key(&offset
.0, |unit
| unit
.offset
.0)
777 // There is never a DIE at the unit offset or before the first unit.
778 Ok(_
) | Err(0) => return Err(gimli
::Error
::NoEntryAtGivenOffset
),
779 Err(i
) => &self.units
[i
- 1].dw_unit
,
782 DebugFile
::Supplementary
=> {
785 .binary_search_by_key(&offset
.0, |unit
| unit
.offset
.0)
787 // There is never a DIE at the unit offset or before the first unit.
788 Ok(_
) | Err(0) => return Err(gimli
::Error
::NoEntryAtGivenOffset
),
789 Err(i
) => &self.sup_units
[i
- 1].dw_unit
,
792 DebugFile
::Dwo
=> return Err(gimli
::Error
::NoEntryAtGivenOffset
),
795 let unit_offset
= offset
796 .to_unit_offset(&unit
.header
)
797 .ok_or(gimli
::Error
::NoEntryAtGivenOffset
)?
;
798 Ok((unit
, unit_offset
))
803 files
: Box
<[String
]>,
804 sequences
: Box
<[LineSequence
]>,
808 fn parse
<R
: gimli
::Reader
>(
809 dw_unit
: &gimli
::Unit
<R
>,
810 ilnp
: gimli
::IncompleteLineProgram
<R
, R
::Offset
>,
811 sections
: &gimli
::Dwarf
<R
>,
812 ) -> Result
<Self, Error
> {
813 let mut sequences
= Vec
::new();
814 let mut sequence_rows
= Vec
::<LineRow
>::new();
815 let mut rows
= ilnp
.rows();
816 while let Some((_
, row
)) = rows
.next_row()?
{
817 if row
.end_sequence() {
818 if let Some(start
) = sequence_rows
.first().map(|x
| x
.address
) {
819 let end
= row
.address();
820 let mut rows
= Vec
::new();
821 mem
::swap(&mut rows
, &mut sequence_rows
);
822 sequences
.push(LineSequence
{
825 rows
: rows
.into_boxed_slice(),
831 let address
= row
.address();
832 let file_index
= row
.file_index();
833 let line
= row
.line().map(NonZeroU64
::get
).unwrap_or(0) as u32;
834 let column
= match row
.column() {
835 gimli
::ColumnType
::LeftEdge
=> 0,
836 gimli
::ColumnType
::Column(x
) => x
.get() as u32,
839 if let Some(last_row
) = sequence_rows
.last_mut() {
840 if last_row
.address
== address
{
841 last_row
.file_index
= file_index
;
842 last_row
.line
= line
;
843 last_row
.column
= column
;
848 sequence_rows
.push(LineRow
{
855 sequences
.sort_by_key(|x
| x
.start
);
857 let mut files
= Vec
::new();
858 let header
= rows
.header();
859 match header
.file(0) {
860 Some(file
) => files
.push(render_file(dw_unit
, file
, header
, sections
)?
),
861 None
=> files
.push(String
::from("")), // DWARF version <= 4 may not have 0th index
864 while let Some(file
) = header
.file(index
) {
865 files
.push(render_file(dw_unit
, file
, header
, sections
)?
);
870 files
: files
.into_boxed_slice(),
871 sequences
: sequences
.into_boxed_slice(),
876 fn render_file
<R
: gimli
::Reader
>(
877 dw_unit
: &gimli
::Unit
<R
>,
878 file
: &gimli
::FileEntry
<R
, R
::Offset
>,
879 header
: &gimli
::LineProgramHeader
<R
, R
::Offset
>,
880 sections
: &gimli
::Dwarf
<R
>,
881 ) -> Result
<String
, gimli
::Error
> {
882 let mut path
= if let Some(ref comp_dir
) = dw_unit
.comp_dir
{
883 comp_dir
.to_string_lossy()?
.into_owned()
888 // The directory index 0 is defined to correspond to the compilation unit directory.
889 if file
.directory_index() != 0 {
890 if let Some(directory
) = file
.directory(header
) {
894 .attr_string(dw_unit
, directory
)?
904 .attr_string(dw_unit
, file
.path_name())?
912 struct LineSequence
{
915 rows
: Box
<[LineRow
]>,
925 /// This struct contains the information needed to find split DWARF data
926 /// and to produce a `gimli::Dwarf<R>` for it.
927 pub struct SplitDwarfLoad
<R
> {
928 /// The dwo id, for looking up in a DWARF package, or for
929 /// verifying an unpacked dwo found on the file system
930 pub dwo_id
: gimli
::DwoId
,
931 /// The compilation directory `path` is relative to.
932 pub comp_dir
: Option
<R
>,
933 /// A path on the filesystem, relative to `comp_dir` to find this dwo.
935 /// Once the split DWARF data is loaded, the loader is expected
936 /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before
937 /// returning the data.
938 pub parent
: Arc
<gimli
::Dwarf
<R
>>,
941 struct SimpleLookup
<T
, R
, F
>
943 F
: FnOnce(Option
<Arc
<gimli
::Dwarf
<R
>>>) -> T
,
947 phantom
: PhantomData
<(T
, R
)>,
950 impl<T
, R
, F
> SimpleLookup
<T
, R
, F
>
952 F
: FnOnce(Option
<Arc
<gimli
::Dwarf
<R
>>>) -> T
,
955 fn new_complete(t
: F
::Output
) -> LookupResult
<SimpleLookup
<T
, R
, F
>> {
956 LookupResult
::Output(t
)
959 fn new_needs_load(load
: SplitDwarfLoad
<R
>, f
: F
) -> LookupResult
<SimpleLookup
<T
, R
, F
>> {
962 continuation
: SimpleLookup
{
964 phantom
: PhantomData
,
970 impl<T
, R
, F
> LookupContinuation
for SimpleLookup
<T
, R
, F
>
972 F
: FnOnce(Option
<Arc
<gimli
::Dwarf
<R
>>>) -> T
,
978 fn resume(self, v
: Option
<Arc
<gimli
::Dwarf
<Self::Buf
>>>) -> LookupResult
<Self> {
979 LookupResult
::Output((self.f
)(v
))
983 struct MappedLookup
<T
, L
, F
>
985 L
: LookupContinuation
,
986 F
: FnOnce(L
::Output
) -> T
,
992 impl<T
, L
, F
> LookupContinuation
for MappedLookup
<T
, L
, F
>
994 L
: LookupContinuation
,
995 F
: FnOnce(L
::Output
) -> T
,
1000 fn resume(self, v
: Option
<Arc
<gimli
::Dwarf
<Self::Buf
>>>) -> LookupResult
<Self> {
1001 match self.original
.resume(v
) {
1002 LookupResult
::Output(t
) => LookupResult
::Output((self.mutator
)(t
)),
1003 LookupResult
::Load { load, continuation }
=> LookupResult
::Load
{
1005 continuation
: MappedLookup
{
1006 original
: continuation
,
1007 mutator
: self.mutator
,
1014 /// Some functions (e.g. `find_frames`) require considering multiple
1015 /// compilation units, each of which might require their own split DWARF
1016 /// lookup (and thus produce a continuation).
1018 /// We store the underlying continuation here as well as a mutator function
1019 /// that will either a) decide that the result of this continuation is
1020 /// what is needed and mutate it to the final result or b) produce another
1021 /// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation
1022 /// `LookupResult` with successive invocations of the mutator, until a new
1023 /// continuation or a final result is produced. And finally, the impl of
1024 /// `LookupContinuation::resume` will call `new_lookup` each time the
1025 /// computation is resumed.
1026 struct LoopingLookup
<T
, L
, F
>
1028 L
: LookupContinuation
,
1029 F
: FnMut(L
::Output
) -> ControlFlow
<T
, LookupResult
<L
>>,
1035 impl<T
, L
, F
> LoopingLookup
<T
, L
, F
>
1037 L
: LookupContinuation
,
1038 F
: FnMut(L
::Output
) -> ControlFlow
<T
, LookupResult
<L
>>,
1040 fn new_complete(t
: T
) -> LookupResult
<Self> {
1041 LookupResult
::Output(t
)
1044 fn new_lookup(mut r
: LookupResult
<L
>, mut mutator
: F
) -> LookupResult
<Self> {
1045 // Drive the loop eagerly so that we only ever have to represent one state
1046 // (the r == ControlFlow::Continue state) in LoopingLookup.
1049 LookupResult
::Output(l
) => match mutator(l
) {
1050 ControlFlow
::Break(t
) => return LookupResult
::Output(t
),
1051 ControlFlow
::Continue(r2
) => {
1055 LookupResult
::Load { load, continuation }
=> {
1056 return LookupResult
::Load
{
1058 continuation
: LoopingLookup
{
1069 impl<T
, L
, F
> LookupContinuation
for LoopingLookup
<T
, L
, F
>
1071 L
: LookupContinuation
,
1072 F
: FnMut(L
::Output
) -> ControlFlow
<T
, LookupResult
<L
>>,
1077 fn resume(self, v
: Option
<Arc
<gimli
::Dwarf
<Self::Buf
>>>) -> LookupResult
<Self> {
1078 let r
= self.continuation
.resume(v
);
1079 LoopingLookup
::new_lookup(r
, self.mutator
)
1083 impl<R
: gimli
::Reader
> ResUnit
<R
> {
1084 fn dwarf_and_unit_dwo
<'unit
, 'ctx
: 'unit
>(
1086 ctx
: &'ctx Context
<R
>,
1089 Result
<(DebugFile
, &'unit gimli
::Dwarf
<R
>, &'unit gimli
::Unit
<R
>), Error
>,
1092 Option
<Arc
<gimli
::Dwarf
<R
>>>,
1094 -> Result
<(DebugFile
, &'unit gimli
::Dwarf
<R
>, &'unit gimli
::Unit
<R
>), Error
>,
1098 break SimpleLookup
::new_complete(match self.dwo
.borrow() {
1099 Some(Ok(Some(v
))) => Ok((DebugFile
::Dwo
, &*v
.0, &v
.1)),
1100 Some(Ok(None
)) => Ok((DebugFile
::Primary
, &*ctx
.sections
, &self.dw_unit
)),
1101 Some(Err(e
)) => Err(*e
),
1103 let dwo_id
= match self.dw_unit
.dwo_id
{
1105 self.dwo
.borrow_with(|| Ok(None
));
1108 Some(dwo_id
) => dwo_id
,
1111 let comp_dir
= self.dw_unit
.comp_dir
.clone();
1113 let dwo_name
= self.dw_unit
.dwo_name().and_then(|s
| {
1114 if let Some(s
) = s
{
1115 Ok(Some(ctx
.sections
.attr_string(&self.dw_unit
, s
)?
))
1121 let path
= match dwo_name
{
1124 self.dwo
.borrow_with(|| Err(e
));
1129 let process_dwo
= move |dwo_dwarf
: Option
<Arc
<gimli
::Dwarf
<R
>>>| {
1130 let dwo_dwarf
= match dwo_dwarf
{
1131 None
=> return Ok(None
),
1132 Some(dwo_dwarf
) => dwo_dwarf
,
1134 let mut dwo_units
= dwo_dwarf
.units();
1135 let dwo_header
= match dwo_units
.next()?
{
1136 Some(dwo_header
) => dwo_header
,
1137 None
=> return Ok(None
),
1140 let mut dwo_unit
= dwo_dwarf
.unit(dwo_header
)?
;
1141 dwo_unit
.copy_relocated_attributes(&self.dw_unit
);
1142 Ok(Some(Box
::new((dwo_dwarf
, dwo_unit
))))
1145 return SimpleLookup
::new_needs_load(
1150 parent
: ctx
.sections
.clone(),
1152 move |dwo_dwarf
| match self.dwo
.borrow_with(|| process_dwo(dwo_dwarf
)) {
1153 Ok(Some(v
)) => Ok((DebugFile
::Dwo
, &*v
.0, &v
.1)),
1154 Ok(None
) => Ok((DebugFile
::Primary
, &*ctx
.sections
, &self.dw_unit
)),
1163 fn parse_lines(&self, sections
: &gimli
::Dwarf
<R
>) -> Result
<Option
<&Lines
>, Error
> {
1164 // NB: line information is always stored in the main debug file so this does not need
1166 let ilnp
= match self.dw_unit
.line_program
{
1167 Some(ref ilnp
) => ilnp
,
1168 None
=> return Ok(None
),
1171 .borrow_with(|| Lines
::parse(&self.dw_unit
, ilnp
.clone(), sections
))
1174 .map_err(Error
::clone
)
1177 fn parse_functions_dwarf_and_unit(
1179 unit
: &gimli
::Unit
<R
>,
1180 sections
: &gimli
::Dwarf
<R
>,
1181 ) -> Result
<&Functions
<R
>, Error
> {
1183 .borrow_with(|| Functions
::parse(unit
, sections
))
1185 .map_err(Error
::clone
)
1188 fn parse_functions
<'unit
, 'ctx
: 'unit
>(
1190 ctx
: &'ctx Context
<R
>,
1191 ) -> LookupResult
<impl LookupContinuation
<Output
= Result
<&'unit Functions
<R
>, Error
>, Buf
= R
>>
1193 self.dwarf_and_unit_dwo(ctx
).map(move |r
| {
1194 let (_file
, sections
, unit
) = r?
;
1195 self.parse_functions_dwarf_and_unit(unit
, sections
)
1198 fn parse_inlined_functions
<'unit
, 'ctx
: 'unit
>(
1200 ctx
: &'ctx Context
<R
>,
1201 ) -> LookupResult
<impl LookupContinuation
<Output
= Result
<(), Error
>, Buf
= R
> + 'unit
> {
1202 self.dwarf_and_unit_dwo(ctx
).map(move |r
| {
1203 let (file
, sections
, unit
) = r?
;
1205 .borrow_with(|| Functions
::parse(unit
, sections
))
1207 .map_err(Error
::clone
)?
1208 .parse_inlined_functions(file
, unit
, ctx
, sections
)
1215 sections
: &gimli
::Dwarf
<R
>,
1216 ) -> Result
<Option
<Location
<'_
>>, Error
> {
1217 if let Some(mut iter
) = LocationRangeUnitIter
::new(self, sections
, probe
, probe
+ 1)?
{
1220 Some((_addr
, _len
, loc
)) => Ok(Some(loc
)),
1228 fn find_location_range(
1232 sections
: &gimli
::Dwarf
<R
>,
1233 ) -> Result
<Option
<LocationRangeUnitIter
<'_
>>, Error
> {
1234 LocationRangeUnitIter
::new(self, sections
, probe_low
, probe_high
)
1237 fn find_function_or_location
<'unit
, 'ctx
: 'unit
>(
1240 ctx
: &'ctx Context
<R
>,
1242 impl LookupContinuation
<
1243 Output
= Result
<(Option
<&'unit Function
<R
>>, Option
<Location
<'unit
>>), Error
>,
1247 self.dwarf_and_unit_dwo(ctx
).map(move |r
| {
1248 let (file
, sections
, unit
) = r?
;
1249 let functions
= self.parse_functions_dwarf_and_unit(unit
, sections
)?
;
1250 let function
= match functions
.find_address(probe
) {
1252 let function_index
= functions
.addresses
[address
].function
;
1253 let (offset
, ref function
) = functions
.functions
[function_index
];
1256 .borrow_with(|| Function
::parse(offset
, file
, unit
, ctx
, sections
))
1258 .map_err(Error
::clone
)?
,
1263 let location
= self.find_location(probe
, sections
)?
;
1264 Ok((function
, location
))
1269 /// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`.
1270 pub struct LocationRangeIter
<'ctx
, R
: gimli
::Reader
> {
1271 unit_iter
: Box
<dyn Iterator
<Item
= (&'ctx ResUnit
<R
>, &'ctx gimli
::Range
)> + 'ctx
>,
1272 iter
: Option
<LocationRangeUnitIter
<'ctx
>>,
1276 sections
: &'ctx gimli
::Dwarf
<R
>,
1279 impl<'ctx
, R
: gimli
::Reader
> LocationRangeIter
<'ctx
, R
> {
1281 fn new(ctx
: &'ctx Context
<R
>, probe_low
: u64, probe_high
: u64) -> Result
<Self, Error
> {
1282 let sections
= &ctx
.sections
;
1283 let unit_iter
= ctx
.find_units_range(probe_low
, probe_high
);
1285 unit_iter
: Box
::new(unit_iter
),
1293 fn next_loc(&mut self) -> Result
<Option
<(u64, u64, Location
<'ctx
>)>, Error
> {
1295 let iter
= self.iter
.take();
1297 None
=> match self.unit_iter
.next() {
1298 Some((unit
, range
)) => {
1299 self.iter
= unit
.find_location_range(
1300 cmp
::max(self.probe_low
, range
.begin
),
1301 cmp
::min(self.probe_high
, range
.end
),
1305 None
=> return Ok(None
),
1308 if let item @
Some(_
) = iter
.next() {
1309 self.iter
= Some(iter
);
1318 impl<'ctx
, R
> Iterator
for LocationRangeIter
<'ctx
, R
>
1320 R
: gimli
::Reader
+ 'ctx
,
1322 type Item
= (u64, u64, Location
<'ctx
>);
1325 fn next(&mut self) -> Option
<Self::Item
> {
1326 match self.next_loc() {
1333 #[cfg(feature = "fallible-iterator")]
1334 impl<'ctx
, R
> fallible_iterator
::FallibleIterator
for LocationRangeIter
<'ctx
, R
>
1336 R
: gimli
::Reader
+ 'ctx
,
1338 type Item
= (u64, u64, Location
<'ctx
>);
1342 fn next(&mut self) -> Result
<Option
<Self::Item
>, Self::Error
> {
1347 struct LocationRangeUnitIter
<'ctx
> {
1349 seqs
: &'ctx
[LineSequence
],
1355 impl<'ctx
> LocationRangeUnitIter
<'ctx
> {
1356 fn new
<R
: gimli
::Reader
>(
1357 resunit
: &'ctx ResUnit
<R
>,
1358 sections
: &gimli
::Dwarf
<R
>,
1361 ) -> Result
<Option
<Self>, Error
> {
1362 let lines
= resunit
.parse_lines(sections
)?
;
1364 if let Some(lines
) = lines
{
1365 // Find index for probe_low.
1366 let seq_idx
= lines
.sequences
.binary_search_by(|sequence
| {
1367 if probe_low
< sequence
.start
{
1369 } else if probe_low
>= sequence
.end
{
1375 let seq_idx
= match seq_idx
{
1377 Err(0) => 0, // probe below sequence, but range could overlap
1378 Err(_
) => lines
.sequences
.len(),
1381 let row_idx
= if let Some(seq
) = lines
.sequences
.get(seq_idx
) {
1382 let idx
= seq
.rows
.binary_search_by(|row
| row
.address
.cmp(&probe_low
));
1385 Err(0) => 0, // probe below sequence, but range could overlap
1394 seqs
: &*lines
.sequences
,
1405 impl<'ctx
> Iterator
for LocationRangeUnitIter
<'ctx
> {
1406 type Item
= (u64, u64, Location
<'ctx
>);
1408 fn next(&mut self) -> Option
<(u64, u64, Location
<'ctx
>)> {
1409 while let Some(seq
) = self.seqs
.get(self.seq_idx
) {
1410 if seq
.start
>= self.probe_high
{
1414 match seq
.rows
.get(self.row_idx
) {
1416 if row
.address
>= self.probe_high
{
1423 .get(row
.file_index
as usize)
1424 .map(String
::as_str
);
1427 .get(self.row_idx
+ 1)
1428 .map(|row
| row
.address
)
1429 .unwrap_or(seq
.end
);
1433 nextaddr
- row
.address
,
1436 line
: if row
.line
!= 0 { Some(row.line) }
else { None }
,
1437 column
: if row
.column
!= 0 {
1458 fn path_push(path
: &mut String
, p
: &str) {
1459 if has_unix_root(p
) || has_windows_root(p
) {
1460 *path
= p
.to_string();
1462 let dir_separator
= if has_windows_root(path
.as_str()) {
1468 if !path
.is_empty() && !path
.ends_with(dir_separator
) {
1469 path
.push(dir_separator
);
1475 /// Check if the path in the given string has a unix style root
1476 fn has_unix_root(p
: &str) -> bool
{
1480 /// Check if the path in the given string has a windows style root
1481 fn has_windows_root(p
: &str) -> bool
{
1482 p
.starts_with('
\\'
) || p
.get(1..3) == Some(":\\")
1484 struct RangeAttributes
<R
: gimli
::Reader
> {
1485 low_pc
: Option
<u64>,
1486 high_pc
: Option
<u64>,
1488 ranges_offset
: Option
<gimli
::RangeListsOffset
<<R
as gimli
::Reader
>::Offset
>>,
1491 impl<R
: gimli
::Reader
> Default
for RangeAttributes
<R
> {
1492 fn default() -> Self {
1497 ranges_offset
: None
,
1502 impl<R
: gimli
::Reader
> RangeAttributes
<R
> {
1503 fn for_each_range
<F
: FnMut(gimli
::Range
)>(
1505 sections
: &gimli
::Dwarf
<R
>,
1506 unit
: &gimli
::Unit
<R
>,
1508 ) -> Result
<bool
, Error
> {
1509 let mut added_any
= false;
1510 let mut add_range
= |range
: gimli
::Range
| {
1511 if range
.begin
< range
.end
{
1516 if let Some(ranges_offset
) = self.ranges_offset
{
1517 let mut range_list
= sections
.ranges(unit
, ranges_offset
)?
;
1518 while let Some(range
) = range_list
.next()?
{
1521 } else if let (Some(begin
), Some(end
)) = (self.low_pc
, self.high_pc
) {
1522 add_range(gimli
::Range { begin, end }
);
1523 } else if let (Some(begin
), Some(size
)) = (self.low_pc
, self.size
) {
1524 add_range(gimli
::Range
{
1533 /// An iterator over function frames.
1534 pub struct FrameIter
<'ctx
, R
>(FrameIterState
<'ctx
, R
>)
1538 enum FrameIterState
<'ctx
, R
>
1543 Location(Option
<Location
<'ctx
>>),
1544 Frames(FrameIterFrames
<'ctx
, R
>),
1547 struct FrameIterFrames
<'ctx
, R
>
1551 unit
: &'ctx ResUnit
<R
>,
1552 sections
: &'ctx gimli
::Dwarf
<R
>,
1553 function
: &'ctx Function
<R
>,
1554 inlined_functions
: iter
::Rev
<maybe_small
::IntoIter
<&'ctx InlinedFunction
<R
>>>,
1555 next
: Option
<Location
<'ctx
>>,
1558 impl<'ctx
, R
> FrameIter
<'ctx
, R
>
1560 R
: gimli
::Reader
+ 'ctx
,
1562 /// Advances the iterator and returns the next frame.
1563 pub fn next(&mut self) -> Result
<Option
<Frame
<'ctx
, R
>>, Error
> {
1564 let frames
= match &mut self.0 {
1565 FrameIterState
::Empty
=> return Ok(None
),
1566 FrameIterState
::Location(location
) => {
1567 // We can't move out of a mutable reference, so use `take` instead.
1568 let location
= location
.take();
1569 self.0 = FrameIterState
::Empty
;
1570 return Ok(Some(Frame
{
1571 dw_die_offset
: None
,
1576 FrameIterState
::Frames(frames
) => frames
,
1579 let loc
= frames
.next
.take();
1580 let func
= match frames
.inlined_functions
.next() {
1584 dw_die_offset
: Some(frames
.function
.dw_die_offset
),
1585 function
: frames
.function
.name
.clone().map(|name
| FunctionName
{
1587 language
: frames
.unit
.lang
,
1591 self.0 = FrameIterState
::Empty
;
1592 return Ok(Some(frame
));
1596 let mut next
= Location
{
1598 line
: if func
.call_line
!= 0 {
1599 Some(func
.call_line
)
1603 column
: if func
.call_column
!= 0 {
1604 Some(func
.call_column
)
1609 if let Some(call_file
) = func
.call_file
{
1610 if let Some(lines
) = frames
.unit
.parse_lines(frames
.sections
)?
{
1611 next
.file
= lines
.files
.get(call_file
as usize).map(String
::as_str
);
1614 frames
.next
= Some(next
);
1617 dw_die_offset
: Some(func
.dw_die_offset
),
1618 function
: func
.name
.clone().map(|name
| FunctionName
{
1620 language
: frames
.unit
.lang
,
1627 #[cfg(feature = "fallible-iterator")]
1628 impl<'ctx
, R
> fallible_iterator
::FallibleIterator
for FrameIter
<'ctx
, R
>
1630 R
: gimli
::Reader
+ 'ctx
,
1632 type Item
= Frame
<'ctx
, R
>;
1636 fn next(&mut self) -> Result
<Option
<Frame
<'ctx
, R
>>, Error
> {
1641 /// A function frame.
1642 pub struct Frame
<'ctx
, R
: gimli
::Reader
> {
1643 /// The DWARF unit offset corresponding to the DIE of the function.
1644 pub dw_die_offset
: Option
<gimli
::UnitOffset
<R
::Offset
>>,
1645 /// The name of the function.
1646 pub function
: Option
<FunctionName
<R
>>,
1647 /// The source location corresponding to this frame.
1648 pub location
: Option
<Location
<'ctx
>>,
1651 /// A function name.
1652 pub struct FunctionName
<R
: gimli
::Reader
> {
1653 /// The name of the function.
1655 /// The language of the compilation unit containing this function.
1656 pub language
: Option
<gimli
::DwLang
>,
1659 impl<R
: gimli
::Reader
> FunctionName
<R
> {
1660 /// The raw name of this function before demangling.
1661 pub fn raw_name(&self) -> Result
<Cow
<'_
, str>, Error
> {
1662 self.name
.to_string_lossy()
1665 /// The name of this function after demangling (if applicable).
1666 pub fn demangle(&self) -> Result
<Cow
<'_
, str>, Error
> {
1667 self.raw_name().map(|x
| demangle_auto(x
, self.language
))
1671 /// Demangle a symbol name using the demangling scheme for the given language.
1673 /// Returns `None` if demangling failed or is not required.
1674 #[allow(unused_variables)]
1675 pub fn demangle(name
: &str, language
: gimli
::DwLang
) -> Option
<String
> {
1677 #[cfg(feature = "rustc-demangle")]
1678 gimli
::DW_LANG_Rust
=> rustc_demangle
::try_demangle(name
)
1681 .map(|x
| format
!("{:#}", x
)),
1682 #[cfg(feature = "cpp_demangle")]
1683 gimli
::DW_LANG_C_plus_plus
1684 | gimli
::DW_LANG_C_plus_plus_03
1685 | gimli
::DW_LANG_C_plus_plus_11
1686 | gimli
::DW_LANG_C_plus_plus_14
=> cpp_demangle
::Symbol
::new(name
)
1688 .and_then(|x
| x
.demangle(&Default
::default()).ok()),
1693 /// Apply 'best effort' demangling of a symbol name.
1695 /// If `language` is given, then only the demangling scheme for that language
1698 /// If `language` is `None`, then heuristics are used to determine how to
1699 /// demangle the name. Currently, these heuristics are very basic.
1701 /// If demangling fails or is not required, then `name` is returned unchanged.
1702 pub fn demangle_auto(name
: Cow
<'_
, str>, language
: Option
<gimli
::DwLang
>) -> Cow
<'_
, str> {
1704 Some(language
) => demangle(name
.as_ref(), language
),
1705 None
=> demangle(name
.as_ref(), gimli
::DW_LANG_Rust
)
1706 .or_else(|| demangle(name
.as_ref(), gimli
::DW_LANG_C_plus_plus
)),
1712 /// A source location.
1713 pub struct Location
<'a
> {
1715 pub file
: Option
<&'a
str>,
1716 /// The line number.
1717 pub line
: Option
<u32>,
1718 /// The column number.
1719 pub column
: Option
<u32>,
1725 fn context_is_send() {
1726 fn assert_is_send
<T
: Send
>() {}
1727 assert_is_send
::<crate::Context
<gimli
::read
::EndianSlice
<'_
, gimli
::LittleEndian
>>>();