]> git.proxmox.com Git - rustc.git/blame - vendor/addr2line-0.16.0/src/lib.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / vendor / addr2line-0.16.0 / src / lib.rs
CommitLineData
f035d41b
XL
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.
5//!
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
10//! efficient.
11//! Location information is obtained with
94222f64
XL
12//! [`Context::find_location`](./struct.Context.html#method.find_location) or
13//! [`Context::find_location_range`](./struct.Context.html#method.find_location_range).
f035d41b
XL
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.
17//!
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/).
21//!
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)]
26#![no_std]
27
28#[allow(unused_imports)]
29#[macro_use]
30extern crate alloc;
31
32#[cfg(feature = "cpp_demangle")]
33extern crate cpp_demangle;
34#[cfg(feature = "fallible-iterator")]
35pub extern crate fallible_iterator;
36pub extern crate gimli;
37#[cfg(feature = "object")]
38pub extern crate object;
39#[cfg(feature = "rustc-demangle")]
40extern crate rustc_demangle;
41
42use alloc::borrow::Cow;
43use alloc::boxed::Box;
44#[cfg(feature = "object")]
45use alloc::rc::Rc;
46use alloc::string::{String, ToString};
94222f64 47use alloc::sync::Arc;
f035d41b
XL
48use alloc::vec::Vec;
49
94222f64 50use core::cmp::{self, Ordering};
f035d41b
XL
51use core::iter;
52use core::mem;
94222f64 53use core::num::NonZeroU64;
f035d41b
XL
54use core::u64;
55
94222f64 56use crate::function::{Function, Functions, InlinedFunction};
f035d41b
XL
57use crate::lazy::LazyCell;
58
59#[cfg(feature = "smallvec")]
60mod maybe_small {
61 pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
62 pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
63}
64#[cfg(not(feature = "smallvec"))]
65mod maybe_small {
66 pub type Vec<T> = alloc::vec::Vec<T>;
67 pub type IntoIter<T> = alloc::vec::IntoIter<T>;
68}
69
94222f64 70mod function;
f035d41b
XL
71mod lazy;
72
73type Error = gimli::Error;
74
75/// The state necessary to perform address to line translation.
76///
77/// Constructing a `Context` is somewhat costly, so users should aim to reuse `Context`s
78/// when performing lookups for many addresses in the same executable.
94222f64
XL
79pub struct Context<R: gimli::Reader> {
80 dwarf: ResDwarf<R>,
f035d41b
XL
81}
82
83/// The type of `Context` that supports the `new` method.
84#[cfg(feature = "std-object")]
85pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>;
86
87#[cfg(feature = "std-object")]
88impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> {
89 /// Construct a new `Context`.
90 ///
91 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
92 /// This means it is not thread safe, has no lifetime constraints (since it copies
93 /// the input data), and works for any endianity.
94 ///
94222f64 95 /// Performance sensitive applications may want to use `Context::from_dwarf`
f035d41b 96 /// with a more specialised `gimli::Reader` implementation.
94222f64 97 #[inline]
fc512014 98 pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>(
f035d41b 99 file: &'file O,
94222f64
XL
100 ) -> Result<Self, Error> {
101 Self::new_with_sup(file, None)
102 }
103
104 /// Construct a new `Context`.
105 ///
106 /// Optionally also use a supplementary object file.
107 ///
108 /// The resulting `Context` uses `gimli::EndianRcSlice<gimli::RunTimeEndian>`.
109 /// This means it is not thread safe, has no lifetime constraints (since it copies
110 /// the input data), and works for any endianity.
111 ///
112 /// Performance sensitive applications may want to use `Context::from_dwarf_with_sup`
113 /// with a more specialised `gimli::Reader` implementation.
114 pub fn new_with_sup<'data: 'file, 'file, O: object::Object<'data, 'file>>(
115 file: &'file O,
116 sup_file: Option<&'file O>,
f035d41b
XL
117 ) -> Result<Self, Error> {
118 let endian = if file.is_little_endian() {
119 gimli::RunTimeEndian::Little
120 } else {
121 gimli::RunTimeEndian::Big
122 };
123
94222f64
XL
124 fn load_section<'data: 'file, 'file, O, Endian>(
125 id: gimli::SectionId,
126 file: &'file O,
127 endian: Endian,
128 ) -> Result<gimli::EndianRcSlice<Endian>, Error>
f035d41b
XL
129 where
130 O: object::Object<'data, 'file>,
f035d41b
XL
131 Endian: gimli::Endianity,
132 {
133 use object::ObjectSection;
134
135 let data = file
94222f64 136 .section_by_name(id.name())
f035d41b
XL
137 .and_then(|section| section.uncompressed_data().ok())
138 .unwrap_or(Cow::Borrowed(&[]));
94222f64 139 Ok(gimli::EndianRcSlice::new(Rc::from(&*data), endian))
f035d41b
XL
140 }
141
94222f64
XL
142 let mut dwarf = gimli::Dwarf::load(|id| load_section(id, file, endian))?;
143 if let Some(sup_file) = sup_file {
144 dwarf.load_sup(|id| load_section(id, sup_file, endian))?;
145 }
146 Context::from_dwarf(dwarf)
f035d41b
XL
147 }
148}
149
150impl<R: gimli::Reader> Context<R> {
151 /// Construct a new `Context` from DWARF sections.
94222f64
XL
152 ///
153 /// This method does not support using a supplementary object file.
f035d41b
XL
154 pub fn from_sections(
155 debug_abbrev: gimli::DebugAbbrev<R>,
156 debug_addr: gimli::DebugAddr<R>,
94222f64 157 debug_aranges: gimli::DebugAranges<R>,
f035d41b
XL
158 debug_info: gimli::DebugInfo<R>,
159 debug_line: gimli::DebugLine<R>,
160 debug_line_str: gimli::DebugLineStr<R>,
161 debug_ranges: gimli::DebugRanges<R>,
162 debug_rnglists: gimli::DebugRngLists<R>,
163 debug_str: gimli::DebugStr<R>,
164 debug_str_offsets: gimli::DebugStrOffsets<R>,
165 default_section: R,
166 ) -> Result<Self, Error> {
167 Self::from_dwarf(gimli::Dwarf {
168 debug_abbrev,
169 debug_addr,
94222f64 170 debug_aranges,
f035d41b
XL
171 debug_info,
172 debug_line,
173 debug_line_str,
174 debug_str,
175 debug_str_offsets,
f035d41b
XL
176 debug_types: default_section.clone().into(),
177 locations: gimli::LocationLists::new(
178 default_section.clone().into(),
179 default_section.clone().into(),
180 ),
181 ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
fc512014 182 file_type: gimli::DwarfFileType::Main,
94222f64 183 sup: None,
f035d41b
XL
184 })
185 }
186
187 /// Construct a new `Context` from an existing [`gimli::Dwarf`] object.
94222f64 188 #[inline]
f035d41b 189 pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Self, Error> {
94222f64
XL
190 let mut dwarf = ResDwarf::parse(Arc::new(sections))?;
191 dwarf.sup = match dwarf.sections.sup.clone() {
192 Some(sup_sections) => Some(Box::new(ResDwarf::parse(sup_sections)?)),
193 None => None,
194 };
195 Ok(Context { dwarf })
f035d41b
XL
196 }
197
198 /// The dwarf sections associated with this `Context`.
199 pub fn dwarf(&self) -> &gimli::Dwarf<R> {
94222f64 200 &self.dwarf.sections
f035d41b
XL
201 }
202
fc512014
XL
203 /// Finds the CUs for the function address given.
204 ///
205 /// There might be multiple CUs whose range contains this address.
206 /// Weak symbols have shown up in the wild which cause this to happen
207 /// but otherwise this can happen if the CU has non-contiguous functions
208 /// but only reports a single range.
209 ///
210 /// Consequently we return an iterator for all CUs which may contain the
211 /// address, and the caller must check if there is actually a function or
212 /// location in the CU for that address.
213 fn find_units(&self, probe: u64) -> impl Iterator<Item = &ResUnit<R>> {
94222f64
XL
214 self.find_units_range(probe, probe + 1)
215 .map(|(unit, _range)| unit)
216 }
217
218 /// Finds the CUs covering the range of addresses given.
219 ///
220 /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple
221 /// ranges for the same unit.
222 #[inline]
223 fn find_units_range(
224 &self,
225 probe_low: u64,
226 probe_high: u64,
227 ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> {
f035d41b
XL
228 // First up find the position in the array which could have our function
229 // address.
230 let pos = match self
94222f64 231 .dwarf
f035d41b 232 .unit_ranges
94222f64 233 .binary_search_by_key(&probe_high, |i| i.range.begin)
f035d41b
XL
234 {
235 // Although unlikely, we could find an exact match.
236 Ok(i) => i + 1,
237 // No exact match was found, but this probe would fit at slot `i`.
238 // This means that slot `i` is bigger than `probe`, along with all
239 // indices greater than `i`, so we need to search all previous
240 // entries.
241 Err(i) => i,
242 };
243
244 // Once we have our index we iterate backwards from that position
245 // looking for a matching CU.
94222f64 246 self.dwarf.unit_ranges[..pos]
fc512014
XL
247 .iter()
248 .rev()
249 .take_while(move |i| {
250 // We know that this CU's start is beneath the probe already because
251 // of our sorted array.
94222f64 252 debug_assert!(i.range.begin <= probe_high);
fc512014
XL
253
254 // Each entry keeps track of the maximum end address seen so far,
255 // starting from the beginning of the array of unit ranges. We're
256 // iterating in reverse so if our probe is beyond the maximum range
257 // of this entry, then it's guaranteed to not fit in any prior
258 // entries, so we break out.
94222f64 259 probe_low < i.max_end
fc512014
XL
260 })
261 .filter_map(move |i| {
262 // If this CU doesn't actually contain this address, move to the
263 // next CU.
94222f64 264 if probe_low >= i.range.end || probe_high <= i.range.begin {
fc512014
XL
265 return None;
266 }
94222f64 267 Some((&self.dwarf.units[i.unit_id], &i.range))
fc512014 268 })
f035d41b
XL
269 }
270
271 /// Find the DWARF unit corresponding to the given virtual memory address.
272 pub fn find_dwarf_unit(&self, probe: u64) -> Option<&gimli::Unit<R>> {
fc512014 273 for unit in self.find_units(probe) {
94222f64 274 match unit.find_function_or_location(probe, &self.dwarf) {
fc512014
XL
275 Ok((Some(_), _)) | Ok((_, Some(_))) => return Some(&unit.dw_unit),
276 _ => {}
277 }
278 }
279 None
f035d41b
XL
280 }
281
282 /// Find the source file and line corresponding to the given virtual memory address.
283 pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
fc512014 284 for unit in self.find_units(probe) {
94222f64 285 if let Some(location) = unit.find_location(probe, &self.dwarf.sections)? {
fc512014
XL
286 return Ok(Some(location));
287 }
f035d41b 288 }
fc512014 289 Ok(None)
f035d41b
XL
290 }
291
94222f64
XL
292 /// Return source file and lines for a range of addresses. For each location it also
293 /// returns the address and size of the range of the underlying instructions.
294 pub fn find_location_range(
295 &self,
296 probe_low: u64,
297 probe_high: u64,
298 ) -> Result<LocationRangeIter<'_, R>, Error> {
299 LocationRangeIter::new(self, probe_low, probe_high)
300 }
301
f035d41b
XL
302 /// Return an iterator for the function frames corresponding to the given virtual
303 /// memory address.
304 ///
305 /// If the probe address is not for an inline function then only one frame is
306 /// returned.
307 ///
308 /// If the probe address is for an inline function then the first frame corresponds
309 /// to the innermost inline function. Subsequent frames contain the caller and call
310 /// location, until an non-inline caller is reached.
311 pub fn find_frames(&self, probe: u64) -> Result<FrameIter<R>, Error> {
fc512014 312 for unit in self.find_units(probe) {
94222f64 313 match unit.find_function_or_location(probe, &self.dwarf)? {
fc512014
XL
314 (Some(function), location) => {
315 let inlined_functions = function.find_inlined_functions(probe);
316 return Ok(FrameIter(FrameIterState::Frames(FrameIterFrames {
317 unit,
94222f64 318 sections: &self.dwarf.sections,
fc512014
XL
319 function,
320 inlined_functions,
321 next: location,
322 })));
f035d41b 323 }
fc512014
XL
324 (None, Some(location)) => {
325 return Ok(FrameIter(FrameIterState::Location(Some(location))));
326 }
327 _ => {}
3dfed10e
XL
328 }
329 }
fc512014 330 Ok(FrameIter(FrameIterState::Empty))
f035d41b
XL
331 }
332
333 /// Initialize all line data structures. This is used for benchmarks.
334 #[doc(hidden)]
335 pub fn parse_lines(&self) -> Result<(), Error> {
94222f64
XL
336 for unit in &self.dwarf.units {
337 unit.parse_lines(&self.dwarf.sections)?;
f035d41b
XL
338 }
339 Ok(())
340 }
341
342 /// Initialize all function data structures. This is used for benchmarks.
343 #[doc(hidden)]
344 pub fn parse_functions(&self) -> Result<(), Error> {
94222f64
XL
345 for unit in &self.dwarf.units {
346 unit.parse_functions(&self.dwarf)?;
3dfed10e
XL
347 }
348 Ok(())
349 }
350
351 /// Initialize all inlined function data structures. This is used for benchmarks.
352 #[doc(hidden)]
353 pub fn parse_inlined_functions(&self) -> Result<(), Error> {
94222f64
XL
354 for unit in &self.dwarf.units {
355 unit.parse_inlined_functions(&self.dwarf)?;
f035d41b
XL
356 }
357 Ok(())
358 }
359}
360
94222f64
XL
361struct UnitRange {
362 unit_id: usize,
363 max_end: u64,
364 range: gimli::Range,
365}
366
367struct ResDwarf<R: gimli::Reader> {
368 unit_ranges: Vec<UnitRange>,
369 units: Vec<ResUnit<R>>,
370 sections: Arc<gimli::Dwarf<R>>,
371 sup: Option<Box<ResDwarf<R>>>,
372}
373
374impl<R: gimli::Reader> ResDwarf<R> {
375 fn parse(sections: Arc<gimli::Dwarf<R>>) -> Result<Self, Error> {
376 // Find all the references to compilation units in .debug_aranges.
377 // Note that we always also iterate through all of .debug_info to
378 // find compilation units, because .debug_aranges may be missing some.
379 let mut aranges = Vec::new();
380 let mut headers = sections.debug_aranges.headers();
381 while let Some(header) = headers.next()? {
382 aranges.push((header.debug_info_offset(), header.offset()));
383 }
384 aranges.sort_by_key(|i| i.0);
385
386 let mut unit_ranges = Vec::new();
387 let mut res_units = Vec::new();
388 let mut units = sections.units();
389 while let Some(header) = units.next()? {
390 let unit_id = res_units.len();
391 let offset = match header.offset().as_debug_info_offset() {
392 Some(offset) => offset,
393 None => continue,
394 };
395 // We mainly want compile units, but we may need to follow references to entries
396 // within other units for function names. We don't need anything from type units.
397 match header.type_() {
398 gimli::UnitType::Type { .. } | gimli::UnitType::SplitType { .. } => continue,
399 _ => {}
400 }
401 let dw_unit = match sections.unit(header) {
402 Ok(dw_unit) => dw_unit,
403 Err(_) => continue,
404 };
405
406 let mut lang = None;
407 {
408 let mut entries = dw_unit.entries_raw(None)?;
409
410 let abbrev = match entries.read_abbreviation()? {
411 Some(abbrev) => abbrev,
412 None => continue,
413 };
414
415 let mut ranges = RangeAttributes::default();
416 for spec in abbrev.attributes() {
417 let attr = entries.read_attribute(*spec)?;
418 match attr.name() {
419 gimli::DW_AT_low_pc => {
420 if let gimli::AttributeValue::Addr(val) = attr.value() {
421 ranges.low_pc = Some(val);
422 }
423 }
424 gimli::DW_AT_high_pc => match attr.value() {
425 gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val),
426 gimli::AttributeValue::Udata(val) => ranges.size = Some(val),
427 _ => {}
428 },
429 gimli::DW_AT_ranges => {
430 ranges.ranges_offset =
431 sections.attr_ranges_offset(&dw_unit, attr.value())?;
432 }
433 gimli::DW_AT_language => {
434 if let gimli::AttributeValue::Language(val) = attr.value() {
435 lang = Some(val);
436 }
437 }
438 _ => {}
439 }
440 }
441
442 // Find the address ranges for the CU, using in order of preference:
443 // - DW_AT_ranges
444 // - .debug_aranges
445 // - DW_AT_low_pc/DW_AT_high_pc
446 //
447 // Using DW_AT_ranges before .debug_aranges is possibly an arbitrary choice,
448 // but the feeling is that DW_AT_ranges is more likely to be reliable or complete
449 // if it is present.
450 //
451 // .debug_aranges must be used before DW_AT_low_pc/DW_AT_high_pc because
452 // it has been observed on macOS that DW_AT_ranges was not emitted even for
453 // discontiguous CUs.
454 let i = match ranges.ranges_offset {
455 Some(_) => None,
456 None => aranges.binary_search_by_key(&offset, |x| x.0).ok(),
457 };
458 if let Some(mut i) = i {
459 // There should be only one set per CU, but in practice multiple
460 // sets have been observed. This is probably a compiler bug, but
461 // either way we need to handle it.
462 while i > 0 && aranges[i - 1].0 == offset {
463 i -= 1;
464 }
465 for (_, aranges_offset) in aranges[i..].iter().take_while(|x| x.0 == offset) {
466 let aranges_header = sections.debug_aranges.header(*aranges_offset)?;
467 let mut aranges = aranges_header.entries();
468 while let Some(arange) = aranges.next()? {
469 if arange.length() != 0 {
470 unit_ranges.push(UnitRange {
471 range: arange.range(),
472 unit_id,
473 max_end: 0,
474 });
475 }
476 }
477 }
478 } else {
479 ranges.for_each_range(&sections, &dw_unit, |range| {
480 unit_ranges.push(UnitRange {
481 range,
482 unit_id,
483 max_end: 0,
484 });
485 })?;
486 }
487 }
488
489 res_units.push(ResUnit {
490 offset,
491 dw_unit,
492 lang,
493 lines: LazyCell::new(),
494 funcs: LazyCell::new(),
495 });
496 }
497
498 // Sort this for faster lookup in `find_unit_and_address` below.
499 unit_ranges.sort_by_key(|i| i.range.begin);
500
501 // Calculate the `max_end` field now that we've determined the order of
502 // CUs.
503 let mut max = 0;
504 for i in unit_ranges.iter_mut() {
505 max = max.max(i.range.end);
506 i.max_end = max;
507 }
508
509 Ok(ResDwarf {
510 units: res_units,
511 unit_ranges,
512 sections,
513 sup: None,
514 })
515 }
516
517 fn find_unit(&self, offset: gimli::DebugInfoOffset<R::Offset>) -> Result<&ResUnit<R>, Error> {
518 match self
519 .units
520 .binary_search_by_key(&offset.0, |unit| unit.offset.0)
521 {
522 // There is never a DIE at the unit offset or before the first unit.
523 Ok(_) | Err(0) => Err(gimli::Error::NoEntryAtGivenOffset),
524 Err(i) => Ok(&self.units[i - 1]),
525 }
526 }
527}
528
f035d41b
XL
529struct Lines {
530 files: Box<[String]>,
531 sequences: Box<[LineSequence]>,
532}
533
534struct LineSequence {
535 start: u64,
536 end: u64,
537 rows: Box<[LineRow]>,
538}
539
540struct LineRow {
541 address: u64,
542 file_index: u64,
543 line: u32,
544 column: u32,
545}
546
94222f64 547struct ResUnit<R: gimli::Reader> {
f035d41b
XL
548 offset: gimli::DebugInfoOffset<R::Offset>,
549 dw_unit: gimli::Unit<R>,
550 lang: Option<gimli::DwLang>,
551 lines: LazyCell<Result<Lines, Error>>,
552 funcs: LazyCell<Result<Functions<R>, Error>>,
553}
554
94222f64 555impl<R: gimli::Reader> ResUnit<R> {
f035d41b
XL
556 fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> {
557 let ilnp = match self.dw_unit.line_program {
558 Some(ref ilnp) => ilnp,
559 None => return Ok(None),
560 };
561 self.lines
562 .borrow_with(|| {
563 let mut sequences = Vec::new();
564 let mut sequence_rows = Vec::<LineRow>::new();
565 let mut rows = ilnp.clone().rows();
566 while let Some((_, row)) = rows.next_row()? {
567 if row.end_sequence() {
568 if let Some(start) = sequence_rows.first().map(|x| x.address) {
569 let end = row.address();
570 let mut rows = Vec::new();
571 mem::swap(&mut rows, &mut sequence_rows);
3dfed10e
XL
572 sequences.push(LineSequence {
573 start,
574 end,
575 rows: rows.into_boxed_slice(),
576 });
f035d41b
XL
577 }
578 continue;
579 }
580
581 let address = row.address();
582 let file_index = row.file_index();
94222f64 583 let line = row.line().map(NonZeroU64::get).unwrap_or(0) as u32;
f035d41b
XL
584 let column = match row.column() {
585 gimli::ColumnType::LeftEdge => 0,
94222f64 586 gimli::ColumnType::Column(x) => x.get() as u32,
f035d41b
XL
587 };
588
589 if let Some(last_row) = sequence_rows.last_mut() {
590 if last_row.address == address {
591 last_row.file_index = file_index;
592 last_row.line = line;
593 last_row.column = column;
594 continue;
595 }
596 }
597
598 sequence_rows.push(LineRow {
599 address,
600 file_index,
601 line,
602 column,
603 });
604 }
605 sequences.sort_by_key(|x| x.start);
606
607 let mut files = Vec::new();
f035d41b 608 let header = ilnp.header();
fc512014
XL
609 match header.file(0) {
610 Some(file) => files.push(self.render_file(file, header, sections)?),
611 None => files.push(String::from("")), // DWARF version <= 4 may not have 0th index
612 }
613 let mut index = 1;
f035d41b
XL
614 while let Some(file) = header.file(index) {
615 files.push(self.render_file(file, header, sections)?);
616 index += 1;
617 }
618
619 Ok(Lines {
620 files: files.into_boxed_slice(),
621 sequences: sequences.into_boxed_slice(),
622 })
623 })
624 .as_ref()
625 .map(Some)
626 .map_err(Error::clone)
627 }
628
94222f64 629 fn parse_functions(&self, dwarf: &ResDwarf<R>) -> Result<&Functions<R>, Error> {
3dfed10e 630 self.funcs
94222f64 631 .borrow_with(|| Functions::parse(&self.dw_unit, dwarf))
3dfed10e
XL
632 .as_ref()
633 .map_err(Error::clone)
634 }
635
94222f64 636 fn parse_inlined_functions(&self, dwarf: &ResDwarf<R>) -> Result<(), Error> {
f035d41b 637 self.funcs
94222f64 638 .borrow_with(|| Functions::parse(&self.dw_unit, dwarf))
f035d41b 639 .as_ref()
3dfed10e 640 .map_err(Error::clone)?
94222f64 641 .parse_inlined_functions(&self.dw_unit, dwarf)
f035d41b
XL
642 }
643
644 fn find_location(
645 &self,
646 probe: u64,
647 sections: &gimli::Dwarf<R>,
648 ) -> Result<Option<Location<'_>>, Error> {
94222f64
XL
649 if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? {
650 match iter.next() {
651 None => Ok(None),
652 Some((_addr, _len, loc)) => Ok(Some(loc)),
17df50a5 653 }
94222f64
XL
654 } else {
655 Ok(None)
656 }
657 }
658
659 #[inline]
660 fn find_location_range(
661 &self,
662 probe_low: u64,
663 probe_high: u64,
664 sections: &gimli::Dwarf<R>,
665 ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> {
666 LocationRangeUnitIter::new(self, sections, probe_low, probe_high)
f035d41b
XL
667 }
668
fc512014
XL
669 fn find_function_or_location(
670 &self,
671 probe: u64,
94222f64 672 dwarf: &ResDwarf<R>,
fc512014 673 ) -> Result<(Option<&Function<R>>, Option<Location<'_>>), Error> {
94222f64
XL
674 let functions = self.parse_functions(dwarf)?;
675 let function = match functions.find_address(probe) {
676 Some(address) => {
677 let function_index = functions.addresses[address].function;
678 let (offset, ref function) = functions.functions[function_index];
679 Some(
680 function
681 .borrow_with(|| Function::parse(offset, &self.dw_unit, dwarf))
682 .as_ref()
683 .map_err(Error::clone)?,
684 )
685 }
686 None => None,
fc512014 687 };
94222f64
XL
688 let location = self.find_location(probe, &dwarf.sections)?;
689 Ok((function, location))
fc512014
XL
690 }
691
f035d41b
XL
692 fn render_file(
693 &self,
694 file: &gimli::FileEntry<R, R::Offset>,
695 header: &gimli::LineProgramHeader<R, R::Offset>,
696 sections: &gimli::Dwarf<R>,
697 ) -> Result<String, gimli::Error> {
698 let mut path = if let Some(ref comp_dir) = self.dw_unit.comp_dir {
699 comp_dir.to_string_lossy()?.into_owned()
700 } else {
701 String::new()
702 };
703
704 if let Some(directory) = file.directory(header) {
705 path_push(
706 &mut path,
707 sections
708 .attr_string(&self.dw_unit, directory)?
709 .to_string_lossy()?
710 .as_ref(),
711 );
712 }
713
714 path_push(
715 &mut path,
716 sections
717 .attr_string(&self.dw_unit, file.path_name())?
718 .to_string_lossy()?
719 .as_ref(),
720 );
721
722 Ok(path)
723 }
724}
725
94222f64
XL
726/// Iterator over `Location`s in a range of addresses, returned by `Context::find_location_range`.
727pub struct LocationRangeIter<'ctx, R: gimli::Reader> {
728 unit_iter: Box<dyn Iterator<Item = (&'ctx ResUnit<R>, &'ctx gimli::Range)> + 'ctx>,
729 iter: Option<LocationRangeUnitIter<'ctx>>,
730
731 probe_low: u64,
732 probe_high: u64,
733 sections: &'ctx gimli::Dwarf<R>,
f035d41b
XL
734}
735
94222f64
XL
736impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> {
737 #[inline]
738 fn new(ctx: &'ctx Context<R>, probe_low: u64, probe_high: u64) -> Result<Self, Error> {
739 let sections = &ctx.dwarf.sections;
740 let unit_iter = ctx.find_units_range(probe_low, probe_high);
741 Ok(Self {
742 unit_iter: Box::new(unit_iter),
743 iter: None,
744 probe_low,
745 probe_high,
746 sections,
747 })
f035d41b
XL
748 }
749
94222f64
XL
750 fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> {
751 loop {
752 let iter = self.iter.take();
753 match iter {
754 None => match self.unit_iter.next() {
755 Some((unit, range)) => {
756 self.iter = unit.find_location_range(
757 cmp::max(self.probe_low, range.begin),
758 cmp::min(self.probe_high, range.end),
759 self.sections,
760 )?;
f035d41b 761 }
94222f64
XL
762 None => return Ok(None),
763 },
764 Some(mut iter) => {
765 if let item @ Some(_) = iter.next() {
766 self.iter = Some(iter);
767 return Ok(item);
f035d41b
XL
768 }
769 }
94222f64 770 }
f035d41b
XL
771 }
772 }
f035d41b
XL
773}
774
94222f64
XL
775impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R>
776where
777 R: gimli::Reader + 'ctx,
778{
779 type Item = (u64, u64, Location<'ctx>);
3dfed10e 780
94222f64
XL
781 #[inline]
782 fn next(&mut self) -> Option<Self::Item> {
783 match self.next_loc() {
784 Err(_) => None,
785 Ok(loc) => loc,
786 }
787 }
f035d41b
XL
788}
789
94222f64
XL
790#[cfg(feature = "fallible-iterator")]
791impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R>
792where
793 R: gimli::Reader + 'ctx,
794{
795 type Item = (u64, u64, Location<'ctx>);
796 type Error = Error;
f035d41b 797
94222f64
XL
798 #[inline]
799 fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
800 self.next_loc()
801 }
3dfed10e
XL
802}
803
94222f64
XL
804struct LocationRangeUnitIter<'ctx> {
805 lines: &'ctx Lines,
806 seqs: &'ctx [LineSequence],
807 seq_idx: usize,
808 row_idx: usize,
809 probe_high: u64,
f035d41b
XL
810}
811
94222f64
XL
812impl<'ctx> LocationRangeUnitIter<'ctx> {
813 fn new<R: gimli::Reader>(
814 resunit: &'ctx ResUnit<R>,
815 sections: &gimli::Dwarf<R>,
816 probe_low: u64,
817 probe_high: u64,
818 ) -> Result<Option<Self>, Error> {
819 let lines = resunit.parse_lines(sections)?;
820
821 if let Some(lines) = lines {
822 // Find index for probe_low.
823 let seq_idx = lines.sequences.binary_search_by(|sequence| {
824 if probe_low < sequence.start {
f035d41b 825 Ordering::Greater
94222f64 826 } else if probe_low >= sequence.end {
f035d41b
XL
827 Ordering::Less
828 } else {
829 Ordering::Equal
830 }
94222f64
XL
831 });
832 let seq_idx = match seq_idx {
833 Ok(x) => x,
834 Err(0) => 0, // probe below sequence, but range could overlap
835 Err(_) => lines.sequences.len(),
836 };
3dfed10e 837
94222f64
XL
838 let row_idx = if let Some(seq) = lines.sequences.get(seq_idx) {
839 let idx = seq.rows.binary_search_by(|row| row.address.cmp(&probe_low));
840 let idx = match idx {
841 Ok(x) => x,
842 Err(0) => 0, // probe below sequence, but range could overlap
843 Err(x) => x - 1,
844 };
845 idx
846 } else {
847 0
848 };
849
850 Ok(Some(Self {
851 lines,
852 seqs: &*lines.sequences,
853 seq_idx,
854 row_idx,
855 probe_high,
856 }))
857 } else {
858 Ok(None)
3dfed10e 859 }
3dfed10e 860 }
f035d41b
XL
861}
862
94222f64
XL
863impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> {
864 type Item = (u64, u64, Location<'ctx>);
f035d41b 865
94222f64 866 fn next(&mut self) -> Option<(u64, u64, Location<'ctx>)> {
f035d41b 867 loop {
94222f64
XL
868 let seq = match self.seqs.get(self.seq_idx) {
869 Some(seq) => seq,
870 None => break,
871 };
3dfed10e 872
94222f64
XL
873 if seq.start >= self.probe_high {
874 break;
3dfed10e 875 }
94222f64
XL
876
877 match seq.rows.get(self.row_idx) {
878 Some(row) => {
879 if row.address >= self.probe_high {
880 break;
3dfed10e 881 }
fc512014 882
94222f64
XL
883 let file = self
884 .lines
885 .files
886 .get(row.file_index as usize)
887 .map(String::as_str);
888 let nextaddr = seq
889 .rows
890 .get(self.row_idx + 1)
891 .map(|row| row.address)
892 .unwrap_or(seq.end);
893
894 let item = (
895 row.address,
896 nextaddr - row.address,
897 Location {
898 file,
899 line: if row.line != 0 { Some(row.line) } else { None },
900 column: if row.column != 0 {
901 Some(row.column)
902 } else {
903 None
904 },
905 },
906 );
907 self.row_idx += 1;
908
909 return Some(item);
910 }
911 None => {
912 self.seq_idx += 1;
913 self.row_idx = 0;
fc512014 914 }
fc512014
XL
915 }
916 }
94222f64 917 None
fc512014 918 }
3dfed10e
XL
919}
920
94222f64
XL
921fn path_push(path: &mut String, p: &str) {
922 if has_unix_root(p) || has_windows_root(p) {
923 *path = p.to_string();
924 } else {
925 let dir_separator = if has_windows_root(path.as_str()) {
926 '\\'
927 } else {
928 '/'
929 };
3dfed10e 930
94222f64
XL
931 if !path.ends_with(dir_separator) {
932 path.push(dir_separator);
933 }
934 *path += p;
3dfed10e
XL
935 }
936}
937
94222f64
XL
938/// Check if the path in the given string has a unix style root
939fn has_unix_root(p: &str) -> bool {
940 p.starts_with('/')
941}
942
943/// Check if the path in the given string has a windows style root
944fn has_windows_root(p: &str) -> bool {
945 p.starts_with('\\') || p.get(1..3) == Some(":\\")
946}
3dfed10e
XL
947struct RangeAttributes<R: gimli::Reader> {
948 low_pc: Option<u64>,
949 high_pc: Option<u64>,
950 size: Option<u64>,
951 ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
952}
953
954impl<R: gimli::Reader> Default for RangeAttributes<R> {
955 fn default() -> Self {
956 RangeAttributes {
957 low_pc: None,
958 high_pc: None,
959 size: None,
960 ranges_offset: None,
961 }
962 }
963}
964
965impl<R: gimli::Reader> RangeAttributes<R> {
966 fn for_each_range<F: FnMut(gimli::Range)>(
967 &self,
968 sections: &gimli::Dwarf<R>,
969 unit: &gimli::Unit<R>,
970 mut f: F,
971 ) -> Result<bool, Error> {
972 let mut added_any = false;
f035d41b 973 let mut add_range = |range: gimli::Range| {
3dfed10e
XL
974 if range.begin < range.end {
975 f(range);
976 added_any = true
f035d41b
XL
977 }
978 };
3dfed10e
XL
979 if let Some(ranges_offset) = self.ranges_offset {
980 let mut range_list = sections.ranges(unit, ranges_offset)?;
981 while let Some(range) = range_list.next()? {
f035d41b
XL
982 add_range(range);
983 }
3dfed10e 984 } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
f035d41b 985 add_range(gimli::Range { begin, end });
3dfed10e 986 } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
f035d41b
XL
987 add_range(gimli::Range {
988 begin,
989 end: begin + size,
990 });
991 }
3dfed10e 992 Ok(added_any)
f035d41b
XL
993 }
994}
995
996/// An iterator over function frames.
3dfed10e
XL
997pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
998where
999 R: gimli::Reader + 'ctx;
1000
1001enum FrameIterState<'ctx, R>
f035d41b
XL
1002where
1003 R: gimli::Reader + 'ctx,
1004{
3dfed10e 1005 Empty,
fc512014 1006 Location(Option<Location<'ctx>>),
3dfed10e
XL
1007 Frames(FrameIterFrames<'ctx, R>),
1008}
1009
1010struct FrameIterFrames<'ctx, R>
1011where
1012 R: gimli::Reader + 'ctx,
1013{
1014 unit: &'ctx ResUnit<R>,
f035d41b 1015 sections: &'ctx gimli::Dwarf<R>,
3dfed10e
XL
1016 function: &'ctx Function<R>,
1017 inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
f035d41b
XL
1018 next: Option<Location<'ctx>>,
1019}
1020
1021impl<'ctx, R> FrameIter<'ctx, R>
1022where
1023 R: gimli::Reader + 'ctx,
1024{
1025 /// Advances the iterator and returns the next frame.
1026 pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
3dfed10e
XL
1027 let frames = match &mut self.0 {
1028 FrameIterState::Empty => return Ok(None),
fc512014
XL
1029 FrameIterState::Location(location) => {
1030 // We can't move out of a mutable reference, so use `take` instead.
1031 let location = location.take();
1032 self.0 = FrameIterState::Empty;
1033 return Ok(Some(Frame {
1034 dw_die_offset: None,
1035 function: None,
1036 location,
1037 }));
1038 }
3dfed10e 1039 FrameIterState::Frames(frames) => frames,
f035d41b
XL
1040 };
1041
3dfed10e
XL
1042 let loc = frames.next.take();
1043 let func = match frames.inlined_functions.next() {
1044 Some(func) => func,
1045 None => {
1046 let frame = Frame {
1047 dw_die_offset: Some(frames.function.dw_die_offset),
1048 function: frames.function.name.clone().map(|name| FunctionName {
1049 name,
1050 language: frames.unit.lang,
1051 }),
1052 location: loc,
1053 };
1054 self.0 = FrameIterState::Empty;
1055 return Ok(Some(frame));
f035d41b 1056 }
3dfed10e 1057 };
f035d41b 1058
3dfed10e
XL
1059 let mut next = Location {
1060 file: None,
1061 line: if func.call_line != 0 {
1062 Some(func.call_line)
1063 } else {
1064 None
1065 },
1066 column: if func.call_column != 0 {
1067 Some(func.call_column)
1068 } else {
1069 None
1070 },
1071 };
1072 if func.call_file != 0 {
1073 if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
1074 next.file = lines.files.get(func.call_file as usize).map(String::as_str);
1075 }
f035d41b 1076 }
3dfed10e 1077 frames.next = Some(next);
f035d41b
XL
1078
1079 Ok(Some(Frame {
1080 dw_die_offset: Some(func.dw_die_offset),
1081 function: func.name.clone().map(|name| FunctionName {
1082 name,
3dfed10e 1083 language: frames.unit.lang,
f035d41b
XL
1084 }),
1085 location: loc,
1086 }))
1087 }
1088}
1089
1090#[cfg(feature = "fallible-iterator")]
1091impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
1092where
1093 R: gimli::Reader + 'ctx,
1094{
1095 type Item = Frame<'ctx, R>;
1096 type Error = Error;
1097
1098 #[inline]
1099 fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
1100 self.next()
1101 }
1102}
1103
1104/// A function frame.
1105pub struct Frame<'ctx, R: gimli::Reader> {
1106 /// The DWARF unit offset corresponding to the DIE of the function.
1107 pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
1108 /// The name of the function.
1109 pub function: Option<FunctionName<R>>,
1110 /// The source location corresponding to this frame.
1111 pub location: Option<Location<'ctx>>,
1112}
1113
1114/// A function name.
1115pub struct FunctionName<R: gimli::Reader> {
1116 /// The name of the function.
1117 pub name: R,
1118 /// The language of the compilation unit containing this function.
1119 pub language: Option<gimli::DwLang>,
1120}
1121
1122impl<R: gimli::Reader> FunctionName<R> {
1123 /// The raw name of this function before demangling.
1124 pub fn raw_name(&self) -> Result<Cow<str>, Error> {
1125 self.name.to_string_lossy()
1126 }
1127
1128 /// The name of this function after demangling (if applicable).
1129 pub fn demangle(&self) -> Result<Cow<str>, Error> {
1130 self.raw_name().map(|x| demangle_auto(x, self.language))
1131 }
1132}
1133
1134/// Demangle a symbol name using the demangling scheme for the given language.
1135///
1136/// Returns `None` if demangling failed or is not required.
1137#[allow(unused_variables)]
1138pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
1139 match language {
1140 #[cfg(feature = "rustc-demangle")]
1141 gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name)
1142 .ok()
1143 .as_ref()
1144 .map(|x| format!("{:#}", x)),
1145 #[cfg(feature = "cpp_demangle")]
1146 gimli::DW_LANG_C_plus_plus
1147 | gimli::DW_LANG_C_plus_plus_03
1148 | gimli::DW_LANG_C_plus_plus_11
1149 | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name)
1150 .ok()
1151 .and_then(|x| x.demangle(&Default::default()).ok()),
1152 _ => None,
1153 }
1154}
1155
1156/// Apply 'best effort' demangling of a symbol name.
1157///
1158/// If `language` is given, then only the demangling scheme for that language
1159/// is used.
1160///
1161/// If `language` is `None`, then heuristics are used to determine how to
1162/// demangle the name. Currently, these heuristics are very basic.
1163///
1164/// If demangling fails or is not required, then `name` is returned unchanged.
1165pub fn demangle_auto(name: Cow<str>, language: Option<gimli::DwLang>) -> Cow<str> {
1166 match language {
1167 Some(language) => demangle(name.as_ref(), language),
1168 None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
1169 .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
1170 }
1171 .map(Cow::from)
1172 .unwrap_or(name)
1173}
1174
1175/// A source location.
1176pub struct Location<'a> {
1177 /// The file name.
1178 pub file: Option<&'a str>,
1179 /// The line number.
1180 pub line: Option<u32>,
1181 /// The column number.
1182 pub column: Option<u32>,
1183}
94222f64
XL
1184
1185#[cfg(test)]
1186mod tests {
1187 #[test]
1188 fn context_is_send() {
1189 fn assert_is_send<T: Send>() {}
1190 assert_is_send::<crate::Context<gimli::read::EndianSlice<gimli::LittleEndian>>>();
1191 }
1192}