| 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 | 
|---|
| 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. | 
|---|
| 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 | #[ cfg(feature = "std")] | 
|---|
| 29 | extern crate std; | 
|---|
| 30 |  | 
|---|
| 31 | #[ allow(unused_imports)] | 
|---|
| 32 | #[ macro_use] | 
|---|
| 33 | extern crate alloc; | 
|---|
| 34 |  | 
|---|
| 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; | 
|---|
| 40 |  | 
|---|
| 41 | use alloc::borrow::Cow; | 
|---|
| 42 | use alloc::boxed::Box; | 
|---|
| 43 | #[ cfg(feature = "object")] | 
|---|
| 44 | use alloc::rc::Rc; | 
|---|
| 45 | use alloc::string::{String, ToString}; | 
|---|
| 46 | use alloc::sync::Arc; | 
|---|
| 47 | use alloc::vec::Vec; | 
|---|
| 48 |  | 
|---|
| 49 | use core::cmp::{self, Ordering}; | 
|---|
| 50 | use core::iter; | 
|---|
| 51 | use core::marker::PhantomData; | 
|---|
| 52 | use core::mem; | 
|---|
| 53 | use core::num::NonZeroU64; | 
|---|
| 54 | use core::ops::ControlFlow; | 
|---|
| 55 | use core::u64; | 
|---|
| 56 |  | 
|---|
| 57 | use crate::function::{Function, Functions, InlinedFunction}; | 
|---|
| 58 | use crate::lazy::LazyCell; | 
|---|
| 59 |  | 
|---|
| 60 | #[ cfg(feature = "smallvec")] | 
|---|
| 61 | mod maybe_small { | 
|---|
| 62 | pub type Vec<T> = smallvec::SmallVec<[T; 16]>; | 
|---|
| 63 | pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>; | 
|---|
| 64 | } | 
|---|
| 65 | #[ cfg(not(feature = "smallvec"))] | 
|---|
| 66 | mod maybe_small { | 
|---|
| 67 | pub type Vec<T> = alloc::vec::Vec<T>; | 
|---|
| 68 | pub type IntoIter<T> = alloc::vec::IntoIter<T>; | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | #[ cfg(all(feature = "std", feature = "object", feature = "memmap2"))] | 
|---|
| 72 | /// A simple builtin split DWARF loader. | 
|---|
| 73 | pub mod builtin_split_dwarf_loader; | 
|---|
| 74 | mod function; | 
|---|
| 75 | mod lazy; | 
|---|
| 76 |  | 
|---|
| 77 | type Error = gimli::Error; | 
|---|
| 78 |  | 
|---|
| 79 | #[ derive(Debug, Clone, Copy, PartialEq, Eq)] | 
|---|
| 80 | enum DebugFile { | 
|---|
| 81 | Primary, | 
|---|
| 82 | Supplementary, | 
|---|
| 83 | Dwo, | 
|---|
| 84 | } | 
|---|
| 85 |  | 
|---|
| 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. | 
|---|
| 91 | /// | 
|---|
| 92 | /// This enum is intended to be used in a loop like so: | 
|---|
| 93 | /// ```no_run | 
|---|
| 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 { | 
|---|
| 101 | ///     match r { | 
|---|
| 102 | ///       LookupResult::Output(result) => break result, | 
|---|
| 103 | ///       LookupResult::Load { load, continuation } => { | 
|---|
| 104 | ///         let dwo = do_split_dwarf_load(load); | 
|---|
| 105 | ///         r = continuation.resume(dwo); | 
|---|
| 106 | ///       } | 
|---|
| 107 | ///     } | 
|---|
| 108 | ///   }; | 
|---|
| 109 | /// ``` | 
|---|
| 110 | pub enum LookupResult<L: LookupContinuation> { | 
|---|
| 111 | /// The lookup requires split DWARF data to be loaded. | 
|---|
| 112 | Load { | 
|---|
| 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. | 
|---|
| 116 | continuation: L, | 
|---|
| 117 | }, | 
|---|
| 118 | /// The lookup has completed and produced an output. | 
|---|
| 119 | Output(<L as LookupContinuation>::Output), | 
|---|
| 120 | } | 
|---|
| 121 |  | 
|---|
| 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 | 
|---|
| 124 | /// API consumer. | 
|---|
| 125 | pub trait LookupContinuation: Sized { | 
|---|
| 126 | /// The final output of this operation. | 
|---|
| 127 | type Output; | 
|---|
| 128 | /// The type of reader used. | 
|---|
| 129 | type Buf: gimli::Reader; | 
|---|
| 130 |  | 
|---|
| 131 | /// Resumes the operation with the provided data. | 
|---|
| 132 | /// | 
|---|
| 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 | 
|---|
| 136 | /// required. | 
|---|
| 137 | /// | 
|---|
| 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>; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 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 { | 
|---|
| 151 | loop { | 
|---|
| 152 | self = match self { | 
|---|
| 153 | LookupResult::Output(t) => return t, | 
|---|
| 154 | LookupResult::Load { continuation, .. } => continuation.resume(None), | 
|---|
| 155 | }; | 
|---|
| 156 | } | 
|---|
| 157 | } | 
|---|
| 158 |  | 
|---|
| 159 | fn map<T, F: FnOnce(L::Output) -> T>(self, f: F) -> LookupResult<MappedLookup<T, L, F>> { | 
|---|
| 160 | match self { | 
|---|
| 161 | LookupResult::Output(t) => LookupResult::Output(f(t)), | 
|---|
| 162 | LookupResult::Load { load, continuation } => LookupResult::Load { | 
|---|
| 163 | load, | 
|---|
| 164 | continuation: MappedLookup { | 
|---|
| 165 | original: continuation, | 
|---|
| 166 | mutator: f, | 
|---|
| 167 | }, | 
|---|
| 168 | }, | 
|---|
| 169 | } | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | fn unwrap(self) -> L::Output { | 
|---|
| 173 | match self { | 
|---|
| 174 | LookupResult::Output(t) => t, | 
|---|
| 175 | LookupResult::Load { .. } => unreachable!( "Internal API misuse"), | 
|---|
| 176 | } | 
|---|
| 177 | } | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | /// The state necessary to perform address to line translation. | 
|---|
| 181 | /// | 
|---|
| 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: Box<[UnitRange]>, | 
|---|
| 187 | units: Box<[ResUnit<R>]>, | 
|---|
| 188 | sup_units: Box<[SupUnit<R>]>, | 
|---|
| 189 | } | 
|---|
| 190 |  | 
|---|
| 191 | /// The type of `Context` that supports the `new` method. | 
|---|
| 192 | #[ cfg(feature = "std-object")] | 
|---|
| 193 | pub type ObjectContext = Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>; | 
|---|
| 194 |  | 
|---|
| 195 | #[ cfg(feature = "std-object")] | 
|---|
| 196 | impl Context<gimli::EndianRcSlice<gimli::RunTimeEndian>> { | 
|---|
| 197 | /// Construct a new `Context`. | 
|---|
| 198 | /// | 
|---|
| 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. | 
|---|
| 202 | /// | 
|---|
| 203 | /// Performance sensitive applications may want to use `Context::from_dwarf` | 
|---|
| 204 | /// with a more specialised `gimli::Reader` implementation. | 
|---|
| 205 | #[ inline] | 
|---|
| 206 | pub fn new<'data: 'file, 'file, O: object::Object<'data, 'file>>( | 
|---|
| 207 | file: &'file O, | 
|---|
| 208 | ) -> Result<Self, Error> { | 
|---|
| 209 | Self::new_with_sup(file, None) | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | /// Construct a new `Context`. | 
|---|
| 213 | /// | 
|---|
| 214 | /// Optionally also use a supplementary object file. | 
|---|
| 215 | /// | 
|---|
| 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. | 
|---|
| 219 | /// | 
|---|
| 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>>( | 
|---|
| 223 | file: &'file O, | 
|---|
| 224 | sup_file: Option<&'file O>, | 
|---|
| 225 | ) -> Result<Self, Error> { | 
|---|
| 226 | let endian = if file.is_little_endian() { | 
|---|
| 227 | gimli::RunTimeEndian::Little | 
|---|
| 228 | } else { | 
|---|
| 229 | gimli::RunTimeEndian::Big | 
|---|
| 230 | }; | 
|---|
| 231 |  | 
|---|
| 232 | fn load_section<'data: 'file, 'file, O, Endian>( | 
|---|
| 233 | id: gimli::SectionId, | 
|---|
| 234 | file: &'file O, | 
|---|
| 235 | endian: Endian, | 
|---|
| 236 | ) -> Result<gimli::EndianRcSlice<Endian>, Error> | 
|---|
| 237 | where | 
|---|
| 238 | O: object::Object<'data, 'file>, | 
|---|
| 239 | Endian: gimli::Endianity, | 
|---|
| 240 | { | 
|---|
| 241 | use object::ObjectSection; | 
|---|
| 242 |  | 
|---|
| 243 | let data = file | 
|---|
| 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)) | 
|---|
| 248 | } | 
|---|
| 249 |  | 
|---|
| 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))?; | 
|---|
| 253 | } | 
|---|
| 254 | Context::from_dwarf(dwarf) | 
|---|
| 255 | } | 
|---|
| 256 | } | 
|---|
| 257 |  | 
|---|
| 258 | impl<R: gimli::Reader> Context<R> { | 
|---|
| 259 | /// Construct a new `Context` from DWARF sections. | 
|---|
| 260 | /// | 
|---|
| 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>, | 
|---|
| 273 | default_section: R, | 
|---|
| 274 | ) -> Result<Self, Error> { | 
|---|
| 275 | Self::from_dwarf(gimli::Dwarf { | 
|---|
| 276 | debug_abbrev, | 
|---|
| 277 | debug_addr, | 
|---|
| 278 | debug_aranges, | 
|---|
| 279 | debug_info, | 
|---|
| 280 | debug_line, | 
|---|
| 281 | debug_line_str, | 
|---|
| 282 | debug_str, | 
|---|
| 283 | debug_str_offsets, | 
|---|
| 284 | debug_types: default_section.clone().into(), | 
|---|
| 285 | locations: gimli::LocationLists::new( | 
|---|
| 286 | default_section.clone().into(), | 
|---|
| 287 | default_section.into(), | 
|---|
| 288 | ), | 
|---|
| 289 | ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists), | 
|---|
| 290 | file_type: gimli::DwarfFileType::Main, | 
|---|
| 291 | sup: None, | 
|---|
| 292 | abbreviations_cache: gimli::AbbreviationsCache::new(), | 
|---|
| 293 | }) | 
|---|
| 294 | } | 
|---|
| 295 |  | 
|---|
| 296 | /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. | 
|---|
| 297 | #[ inline] | 
|---|
| 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)? | 
|---|
| 303 | } else { | 
|---|
| 304 | Vec::new() | 
|---|
| 305 | }; | 
|---|
| 306 | Ok(Context { | 
|---|
| 307 | sections, | 
|---|
| 308 | unit_ranges: unit_ranges.into_boxed_slice(), | 
|---|
| 309 | units: units.into_boxed_slice(), | 
|---|
| 310 | sup_units: sup_units.into_boxed_slice(), | 
|---|
| 311 | }) | 
|---|
| 312 | } | 
|---|
| 313 |  | 
|---|
| 314 | /// Finds the CUs for the function address given. | 
|---|
| 315 | /// | 
|---|
| 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. | 
|---|
| 320 | /// | 
|---|
| 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) | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | /// Finds the CUs covering the range of addresses given. | 
|---|
| 330 | /// | 
|---|
| 331 | /// The range is [low, high) (ie, the upper bound is exclusive). This can return multiple | 
|---|
| 332 | /// ranges for the same unit. | 
|---|
| 333 | #[ inline] | 
|---|
| 334 | fn find_units_range( | 
|---|
| 335 | &self, | 
|---|
| 336 | probe_low: u64, | 
|---|
| 337 | probe_high: u64, | 
|---|
| 338 | ) -> impl Iterator<Item = (&ResUnit<R>, &gimli::Range)> { | 
|---|
| 339 | // First up find the position in the array which could have our function | 
|---|
| 340 | // address. | 
|---|
| 341 | let pos = match self | 
|---|
| 342 | .unit_ranges | 
|---|
| 343 | .binary_search_by_key(&probe_high, |i| i.range.begin) | 
|---|
| 344 | { | 
|---|
| 345 | // Although unlikely, we could find an exact match. | 
|---|
| 346 | Ok(i) => i + 1, | 
|---|
| 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 | 
|---|
| 350 | // entries. | 
|---|
| 351 | Err(i) => i, | 
|---|
| 352 | }; | 
|---|
| 353 |  | 
|---|
| 354 | // Once we have our index we iterate backwards from that position | 
|---|
| 355 | // looking for a matching CU. | 
|---|
| 356 | self.unit_ranges[..pos] | 
|---|
| 357 | .iter() | 
|---|
| 358 | .rev() | 
|---|
| 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); | 
|---|
| 363 |  | 
|---|
| 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 | 
|---|
| 370 | }) | 
|---|
| 371 | .filter_map(move |i| { | 
|---|
| 372 | // If this CU doesn't actually contain this address, move to the | 
|---|
| 373 | // next CU. | 
|---|
| 374 | if probe_low >= i.range.end || probe_high <= i.range.begin { | 
|---|
| 375 | return None; | 
|---|
| 376 | } | 
|---|
| 377 | Some((&self.units[i.unit_id], &i.range)) | 
|---|
| 378 | }) | 
|---|
| 379 | } | 
|---|
| 380 |  | 
|---|
| 381 | /// Find the DWARF unit corresponding to the given virtual memory address. | 
|---|
| 382 | pub fn find_dwarf_and_unit( | 
|---|
| 383 | &self, | 
|---|
| 384 | probe: u64, | 
|---|
| 385 | ) -> LookupResult< | 
|---|
| 386 | impl LookupContinuation<Output = Option<(&gimli::Dwarf<R>, &gimli::Unit<R>)>, Buf = R>, | 
|---|
| 387 | > { | 
|---|
| 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), | 
|---|
| 392 | move |r| { | 
|---|
| 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. | 
|---|
| 398 | .unwrap() | 
|---|
| 399 | .unwrap(); | 
|---|
| 400 | Some((sections, unit)) | 
|---|
| 401 | } | 
|---|
| 402 | _ => match units_iter.next() { | 
|---|
| 403 | Some(next_unit) => { | 
|---|
| 404 | return ControlFlow::Continue( | 
|---|
| 405 | next_unit.find_function_or_location(probe, self), | 
|---|
| 406 | ); | 
|---|
| 407 | } | 
|---|
| 408 | None => None, | 
|---|
| 409 | }, | 
|---|
| 410 | }) | 
|---|
| 411 | }, | 
|---|
| 412 | ); | 
|---|
| 413 | } | 
|---|
| 414 |  | 
|---|
| 415 | LoopingLookup::new_complete(None) | 
|---|
| 416 | } | 
|---|
| 417 |  | 
|---|
| 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)); | 
|---|
| 423 | } | 
|---|
| 424 | } | 
|---|
| 425 | Ok(None) | 
|---|
| 426 | } | 
|---|
| 427 |  | 
|---|
| 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( | 
|---|
| 431 | &self, | 
|---|
| 432 | probe_low: u64, | 
|---|
| 433 | probe_high: u64, | 
|---|
| 434 | ) -> Result<LocationRangeIter<'_, R>, Error> { | 
|---|
| 435 | LocationRangeIter::new(self, probe_low, probe_high) | 
|---|
| 436 | } | 
|---|
| 437 |  | 
|---|
| 438 | /// Return an iterator for the function frames corresponding to the given virtual | 
|---|
| 439 | /// memory address. | 
|---|
| 440 | /// | 
|---|
| 441 | /// If the probe address is not for an inline function then only one frame is | 
|---|
| 442 | /// returned. | 
|---|
| 443 | /// | 
|---|
| 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. | 
|---|
| 447 | pub fn find_frames( | 
|---|
| 448 | &self, | 
|---|
| 449 | probe: u64, | 
|---|
| 450 | ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>> | 
|---|
| 451 | { | 
|---|
| 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 { | 
|---|
| 456 | Err(e) => Err(e), | 
|---|
| 457 | Ok((Some(function), location)) => { | 
|---|
| 458 | let inlined_functions = function.find_inlined_functions(probe); | 
|---|
| 459 | Ok(FrameIter(FrameIterState::Frames(FrameIterFrames { | 
|---|
| 460 | unit, | 
|---|
| 461 | sections: &self.sections, | 
|---|
| 462 | function, | 
|---|
| 463 | inlined_functions, | 
|---|
| 464 | next: location, | 
|---|
| 465 | }))) | 
|---|
| 466 | } | 
|---|
| 467 | Ok((None, Some(location))) => { | 
|---|
| 468 | Ok(FrameIter(FrameIterState::Location(Some(location)))) | 
|---|
| 469 | } | 
|---|
| 470 | Ok((None, None)) => match units_iter.next() { | 
|---|
| 471 | Some(next_unit) => { | 
|---|
| 472 | return ControlFlow::Continue( | 
|---|
| 473 | next_unit.find_function_or_location(probe, self), | 
|---|
| 474 | ); | 
|---|
| 475 | } | 
|---|
| 476 | None => Ok(FrameIter(FrameIterState::Empty)), | 
|---|
| 477 | }, | 
|---|
| 478 | }) | 
|---|
| 479 | }) | 
|---|
| 480 | } else { | 
|---|
| 481 | LoopingLookup::new_complete(Ok(FrameIter(FrameIterState::Empty))) | 
|---|
| 482 | } | 
|---|
| 483 | } | 
|---|
| 484 |  | 
|---|
| 485 | /// Preload units for `probe`. | 
|---|
| 486 | /// | 
|---|
| 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. | 
|---|
| 490 | /// | 
|---|
| 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. | 
|---|
| 494 | /// | 
|---|
| 495 | /// ```no_run | 
|---|
| 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); | 
|---|
| 503 | ///     callback(dwo); | 
|---|
| 504 | ///   }); | 
|---|
| 505 | /// | 
|---|
| 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"), | 
|---|
| 509 | ///   }; | 
|---|
| 510 | /// | 
|---|
| 511 | ///   // ... | 
|---|
| 512 | /// ``` | 
|---|
| 513 | pub fn preload_units( | 
|---|
| 514 | &'_ self, | 
|---|
| 515 | probe: u64, | 
|---|
| 516 | ) -> impl Iterator< | 
|---|
| 517 | Item = ( | 
|---|
| 518 | SplitDwarfLoad<R>, | 
|---|
| 519 | impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_, | 
|---|
| 520 | ), | 
|---|
| 521 | > { | 
|---|
| 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(|_| ()) | 
|---|
| 527 | })), | 
|---|
| 528 | }) | 
|---|
| 529 | } | 
|---|
| 530 |  | 
|---|
| 531 | /// Initialize all line data structures. This is used for benchmarks. | 
|---|
| 532 | #[ doc(hidden)] | 
|---|
| 533 | pub fn parse_lines(&self) -> Result<(), Error> { | 
|---|
| 534 | for unit in self.units.iter() { | 
|---|
| 535 | unit.parse_lines(&self.sections)?; | 
|---|
| 536 | } | 
|---|
| 537 | Ok(()) | 
|---|
| 538 | } | 
|---|
| 539 |  | 
|---|
| 540 | /// Initialize all function data structures. This is used for benchmarks. | 
|---|
| 541 | #[ doc(hidden)] | 
|---|
| 542 | pub fn parse_functions(&self) -> Result<(), Error> { | 
|---|
| 543 | for unit in self.units.iter() { | 
|---|
| 544 | unit.parse_functions(self).skip_all_loads()?; | 
|---|
| 545 | } | 
|---|
| 546 | Ok(()) | 
|---|
| 547 | } | 
|---|
| 548 |  | 
|---|
| 549 | /// Initialize all inlined function data structures. This is used for benchmarks. | 
|---|
| 550 | #[ doc(hidden)] | 
|---|
| 551 | pub fn parse_inlined_functions(&self) -> Result<(), Error> { | 
|---|
| 552 | for unit in self.units.iter() { | 
|---|
| 553 | unit.parse_inlined_functions(self).skip_all_loads()?; | 
|---|
| 554 | } | 
|---|
| 555 | Ok(()) | 
|---|
| 556 | } | 
|---|
| 557 | } | 
|---|
| 558 |  | 
|---|
| 559 | struct UnitRange { | 
|---|
| 560 | unit_id: usize, | 
|---|
| 561 | max_end: u64, | 
|---|
| 562 | range: gimli::Range, | 
|---|
| 563 | } | 
|---|
| 564 |  | 
|---|
| 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>>, | 
|---|
| 572 | } | 
|---|
| 573 |  | 
|---|
| 574 | struct SupUnit<R: gimli::Reader> { | 
|---|
| 575 | offset: gimli::DebugInfoOffset<R::Offset>, | 
|---|
| 576 | dw_unit: gimli::Unit<R>, | 
|---|
| 577 | } | 
|---|
| 578 |  | 
|---|
| 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())); | 
|---|
| 588 | } | 
|---|
| 589 | aranges.sort_by_key(|i| i.0); | 
|---|
| 590 |  | 
|---|
| 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, | 
|---|
| 598 | None => continue, | 
|---|
| 599 | }; | 
|---|
| 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, | 
|---|
| 604 | _ => {} | 
|---|
| 605 | } | 
|---|
| 606 | let dw_unit = match sections.unit(header) { | 
|---|
| 607 | Ok(dw_unit) => dw_unit, | 
|---|
| 608 | Err(_) => continue, | 
|---|
| 609 | }; | 
|---|
| 610 |  | 
|---|
| 611 | let mut lang = None; | 
|---|
| 612 | let mut have_unit_range = false; | 
|---|
| 613 | { | 
|---|
| 614 | let mut entries = dw_unit.entries_raw(None)?; | 
|---|
| 615 |  | 
|---|
| 616 | let abbrev = match entries.read_abbreviation()? { | 
|---|
| 617 | Some(abbrev) => abbrev, | 
|---|
| 618 | None => continue, | 
|---|
| 619 | }; | 
|---|
| 620 |  | 
|---|
| 621 | let mut ranges = RangeAttributes::default(); | 
|---|
| 622 | for spec in abbrev.attributes() { | 
|---|
| 623 | let attr = entries.read_attribute(*spec)?; | 
|---|
| 624 | match attr.name() { | 
|---|
| 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)?); | 
|---|
| 629 | } | 
|---|
| 630 | _ => {} | 
|---|
| 631 | }, | 
|---|
| 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)?); | 
|---|
| 636 | } | 
|---|
| 637 | gimli::AttributeValue::Udata(val) => ranges.size = Some(val), | 
|---|
| 638 | _ => {} | 
|---|
| 639 | }, | 
|---|
| 640 | gimli::DW_AT_ranges => { | 
|---|
| 641 | ranges.ranges_offset = | 
|---|
| 642 | sections.attr_ranges_offset(&dw_unit, attr.value())?; | 
|---|
| 643 | } | 
|---|
| 644 | gimli::DW_AT_language => { | 
|---|
| 645 | if let gimli::AttributeValue::Language(val) = attr.value() { | 
|---|
| 646 | lang = Some(val); | 
|---|
| 647 | } | 
|---|
| 648 | } | 
|---|
| 649 | _ => {} | 
|---|
| 650 | } | 
|---|
| 651 | } | 
|---|
| 652 |  | 
|---|
| 653 | // Find the address ranges for the CU, using in order of preference: | 
|---|
| 654 | // - DW_AT_ranges | 
|---|
| 655 | // - .debug_aranges | 
|---|
| 656 | // - DW_AT_low_pc/DW_AT_high_pc | 
|---|
| 657 | // | 
|---|
| 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 | 
|---|
| 660 | // if it is present. | 
|---|
| 661 | // | 
|---|
| 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 { | 
|---|
| 666 | Some(_) => None, | 
|---|
| 667 | None => aranges.binary_search_by_key(&offset, |x| x.0).ok(), | 
|---|
| 668 | }; | 
|---|
| 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 { | 
|---|
| 674 | i -= 1; | 
|---|
| 675 | } | 
|---|
| 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(), | 
|---|
| 683 | unit_id, | 
|---|
| 684 | max_end: 0, | 
|---|
| 685 | }); | 
|---|
| 686 | have_unit_range = true; | 
|---|
| 687 | } | 
|---|
| 688 | } | 
|---|
| 689 | } | 
|---|
| 690 | } else { | 
|---|
| 691 | have_unit_range |= ranges.for_each_range(sections, &dw_unit, |range| { | 
|---|
| 692 | unit_ranges.push(UnitRange { | 
|---|
| 693 | range, | 
|---|
| 694 | unit_id, | 
|---|
| 695 | max_end: 0, | 
|---|
| 696 | }); | 
|---|
| 697 | })?; | 
|---|
| 698 | } | 
|---|
| 699 | } | 
|---|
| 700 |  | 
|---|
| 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)) | 
|---|
| 708 | .as_ref() | 
|---|
| 709 | { | 
|---|
| 710 | for sequence in lines.sequences.iter() { | 
|---|
| 711 | unit_ranges.push(UnitRange { | 
|---|
| 712 | range: gimli::Range { | 
|---|
| 713 | begin: sequence.start, | 
|---|
| 714 | end: sequence.end, | 
|---|
| 715 | }, | 
|---|
| 716 | unit_id, | 
|---|
| 717 | max_end: 0, | 
|---|
| 718 | }) | 
|---|
| 719 | } | 
|---|
| 720 | } | 
|---|
| 721 | } | 
|---|
| 722 | } | 
|---|
| 723 |  | 
|---|
| 724 | res_units.push(ResUnit { | 
|---|
| 725 | offset, | 
|---|
| 726 | dw_unit, | 
|---|
| 727 | lang, | 
|---|
| 728 | lines, | 
|---|
| 729 | funcs: LazyCell::new(), | 
|---|
| 730 | dwo: LazyCell::new(), | 
|---|
| 731 | }); | 
|---|
| 732 | } | 
|---|
| 733 |  | 
|---|
| 734 | // Sort this for faster lookup in `find_unit_and_address` below. | 
|---|
| 735 | unit_ranges.sort_by_key(|i| i.range.begin); | 
|---|
| 736 |  | 
|---|
| 737 | // Calculate the `max_end` field now that we've determined the order of | 
|---|
| 738 | // CUs. | 
|---|
| 739 | let mut max = 0; | 
|---|
| 740 | for i in unit_ranges.iter_mut() { | 
|---|
| 741 | max = max.max(i.range.end); | 
|---|
| 742 | i.max_end = max; | 
|---|
| 743 | } | 
|---|
| 744 |  | 
|---|
| 745 | Ok((unit_ranges, res_units)) | 
|---|
| 746 | } | 
|---|
| 747 |  | 
|---|
| 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, | 
|---|
| 754 | None => continue, | 
|---|
| 755 | }; | 
|---|
| 756 | let dw_unit = match sections.unit(header) { | 
|---|
| 757 | Ok(dw_unit) => dw_unit, | 
|---|
| 758 | Err(_) => continue, | 
|---|
| 759 | }; | 
|---|
| 760 | sup_units.push(SupUnit { dw_unit, offset }); | 
|---|
| 761 | } | 
|---|
| 762 | Ok(sup_units) | 
|---|
| 763 | } | 
|---|
| 764 |  | 
|---|
| 765 | // Find the unit containing the given offset, and convert the offset into a unit offset. | 
|---|
| 766 | fn find_unit( | 
|---|
| 767 | &self, | 
|---|
| 768 | offset: gimli::DebugInfoOffset<R::Offset>, | 
|---|
| 769 | file: DebugFile, | 
|---|
| 770 | ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> { | 
|---|
| 771 | let unit = match file { | 
|---|
| 772 | DebugFile::Primary => { | 
|---|
| 773 | match self | 
|---|
| 774 | .units | 
|---|
| 775 | .binary_search_by_key(&offset.0, |unit| unit.offset.0) | 
|---|
| 776 | { | 
|---|
| 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, | 
|---|
| 780 | } | 
|---|
| 781 | } | 
|---|
| 782 | DebugFile::Supplementary => { | 
|---|
| 783 | match self | 
|---|
| 784 | .sup_units | 
|---|
| 785 | .binary_search_by_key(&offset.0, |unit| unit.offset.0) | 
|---|
| 786 | { | 
|---|
| 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, | 
|---|
| 790 | } | 
|---|
| 791 | } | 
|---|
| 792 | DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset), | 
|---|
| 793 | }; | 
|---|
| 794 |  | 
|---|
| 795 | let unit_offset = offset | 
|---|
| 796 | .to_unit_offset(&unit.header) | 
|---|
| 797 | .ok_or(gimli::Error::NoEntryAtGivenOffset)?; | 
|---|
| 798 | Ok((unit, unit_offset)) | 
|---|
| 799 | } | 
|---|
| 800 | } | 
|---|
| 801 |  | 
|---|
| 802 | struct Lines { | 
|---|
| 803 | files: Box<[String]>, | 
|---|
| 804 | sequences: Box<[LineSequence]>, | 
|---|
| 805 | } | 
|---|
| 806 |  | 
|---|
| 807 | impl Lines { | 
|---|
| 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 { | 
|---|
| 823 | start, | 
|---|
| 824 | end, | 
|---|
| 825 | rows: rows.into_boxed_slice(), | 
|---|
| 826 | }); | 
|---|
| 827 | } | 
|---|
| 828 | continue; | 
|---|
| 829 | } | 
|---|
| 830 |  | 
|---|
| 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, | 
|---|
| 837 | }; | 
|---|
| 838 |  | 
|---|
| 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; | 
|---|
| 844 | continue; | 
|---|
| 845 | } | 
|---|
| 846 | } | 
|---|
| 847 |  | 
|---|
| 848 | sequence_rows.push(LineRow { | 
|---|
| 849 | address, | 
|---|
| 850 | file_index, | 
|---|
| 851 | line, | 
|---|
| 852 | column, | 
|---|
| 853 | }); | 
|---|
| 854 | } | 
|---|
| 855 | sequences.sort_by_key(|x| x.start); | 
|---|
| 856 |  | 
|---|
| 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 | 
|---|
| 862 | } | 
|---|
| 863 | let mut index = 1; | 
|---|
| 864 | while let Some(file) = header.file(index) { | 
|---|
| 865 | files.push(render_file(dw_unit, file, header, sections)?); | 
|---|
| 866 | index += 1; | 
|---|
| 867 | } | 
|---|
| 868 |  | 
|---|
| 869 | Ok(Self { | 
|---|
| 870 | files: files.into_boxed_slice(), | 
|---|
| 871 | sequences: sequences.into_boxed_slice(), | 
|---|
| 872 | }) | 
|---|
| 873 | } | 
|---|
| 874 | } | 
|---|
| 875 |  | 
|---|
| 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() | 
|---|
| 884 | } else { | 
|---|
| 885 | String::new() | 
|---|
| 886 | }; | 
|---|
| 887 |  | 
|---|
| 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) { | 
|---|
| 891 | path_push( | 
|---|
| 892 | &mut path, | 
|---|
| 893 | sections | 
|---|
| 894 | .attr_string(dw_unit, directory)? | 
|---|
| 895 | .to_string_lossy()? | 
|---|
| 896 | .as_ref(), | 
|---|
| 897 | ); | 
|---|
| 898 | } | 
|---|
| 899 | } | 
|---|
| 900 |  | 
|---|
| 901 | path_push( | 
|---|
| 902 | &mut path, | 
|---|
| 903 | sections | 
|---|
| 904 | .attr_string(dw_unit, file.path_name())? | 
|---|
| 905 | .to_string_lossy()? | 
|---|
| 906 | .as_ref(), | 
|---|
| 907 | ); | 
|---|
| 908 |  | 
|---|
| 909 | Ok(path) | 
|---|
| 910 | } | 
|---|
| 911 |  | 
|---|
| 912 | struct LineSequence { | 
|---|
| 913 | start: u64, | 
|---|
| 914 | end: u64, | 
|---|
| 915 | rows: Box<[LineRow]>, | 
|---|
| 916 | } | 
|---|
| 917 |  | 
|---|
| 918 | struct LineRow { | 
|---|
| 919 | address: u64, | 
|---|
| 920 | file_index: u64, | 
|---|
| 921 | line: u32, | 
|---|
| 922 | column: u32, | 
|---|
| 923 | } | 
|---|
| 924 |  | 
|---|
| 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. | 
|---|
| 934 | pub path: Option<R>, | 
|---|
| 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>>, | 
|---|
| 939 | } | 
|---|
| 940 |  | 
|---|
| 941 | struct SimpleLookup<T, R, F> | 
|---|
| 942 | where | 
|---|
| 943 | F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, | 
|---|
| 944 | R: gimli::Reader, | 
|---|
| 945 | { | 
|---|
| 946 | f: F, | 
|---|
| 947 | phantom: PhantomData<(T, R)>, | 
|---|
| 948 | } | 
|---|
| 949 |  | 
|---|
| 950 | impl<T, R, F> SimpleLookup<T, R, F> | 
|---|
| 951 | where | 
|---|
| 952 | F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, | 
|---|
| 953 | R: gimli::Reader, | 
|---|
| 954 | { | 
|---|
| 955 | fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> { | 
|---|
| 956 | LookupResult::Output(t) | 
|---|
| 957 | } | 
|---|
| 958 |  | 
|---|
| 959 | fn new_needs_load(load: SplitDwarfLoad<R>, f: F) -> LookupResult<SimpleLookup<T, R, F>> { | 
|---|
| 960 | LookupResult::Load { | 
|---|
| 961 | load, | 
|---|
| 962 | continuation: SimpleLookup { | 
|---|
| 963 | f, | 
|---|
| 964 | phantom: PhantomData, | 
|---|
| 965 | }, | 
|---|
| 966 | } | 
|---|
| 967 | } | 
|---|
| 968 | } | 
|---|
| 969 |  | 
|---|
| 970 | impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F> | 
|---|
| 971 | where | 
|---|
| 972 | F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T, | 
|---|
| 973 | R: gimli::Reader, | 
|---|
| 974 | { | 
|---|
| 975 | type Output = T; | 
|---|
| 976 | type Buf = R; | 
|---|
| 977 |  | 
|---|
| 978 | fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { | 
|---|
| 979 | LookupResult::Output((self.f)(v)) | 
|---|
| 980 | } | 
|---|
| 981 | } | 
|---|
| 982 |  | 
|---|
| 983 | struct MappedLookup<T, L, F> | 
|---|
| 984 | where | 
|---|
| 985 | L: LookupContinuation, | 
|---|
| 986 | F: FnOnce(L::Output) -> T, | 
|---|
| 987 | { | 
|---|
| 988 | original: L, | 
|---|
| 989 | mutator: F, | 
|---|
| 990 | } | 
|---|
| 991 |  | 
|---|
| 992 | impl<T, L, F> LookupContinuation for MappedLookup<T, L, F> | 
|---|
| 993 | where | 
|---|
| 994 | L: LookupContinuation, | 
|---|
| 995 | F: FnOnce(L::Output) -> T, | 
|---|
| 996 | { | 
|---|
| 997 | type Output = T; | 
|---|
| 998 | type Buf = L::Buf; | 
|---|
| 999 |  | 
|---|
| 1000 | fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { | 
|---|
| 1001 | match self.original.resume(input:v) { | 
|---|
| 1002 | LookupResult::Output(t: ::Output) => LookupResult::Output((self.mutator)(t)), | 
|---|
| 1003 | LookupResult::Load { load: SplitDwarfLoad<::Buf>, continuation: L } => LookupResult::Load { | 
|---|
| 1004 | load, | 
|---|
| 1005 | continuation: MappedLookup { | 
|---|
| 1006 | original: continuation, | 
|---|
| 1007 | mutator: self.mutator, | 
|---|
| 1008 | }, | 
|---|
| 1009 | }, | 
|---|
| 1010 | } | 
|---|
| 1011 | } | 
|---|
| 1012 | } | 
|---|
| 1013 |  | 
|---|
| 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). | 
|---|
| 1017 | /// | 
|---|
| 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> | 
|---|
| 1027 | where | 
|---|
| 1028 | L: LookupContinuation, | 
|---|
| 1029 | F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, | 
|---|
| 1030 | { | 
|---|
| 1031 | continuation: L, | 
|---|
| 1032 | mutator: F, | 
|---|
| 1033 | } | 
|---|
| 1034 |  | 
|---|
| 1035 | impl<T, L, F> LoopingLookup<T, L, F> | 
|---|
| 1036 | where | 
|---|
| 1037 | L: LookupContinuation, | 
|---|
| 1038 | F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, | 
|---|
| 1039 | { | 
|---|
| 1040 | fn new_complete(t: T) -> LookupResult<Self> { | 
|---|
| 1041 | LookupResult::Output(t) | 
|---|
| 1042 | } | 
|---|
| 1043 |  | 
|---|
| 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. | 
|---|
| 1047 | loop { | 
|---|
| 1048 | match r { | 
|---|
| 1049 | LookupResult::Output(l) => match mutator(l) { | 
|---|
| 1050 | ControlFlow::Break(t) => return LookupResult::Output(t), | 
|---|
| 1051 | ControlFlow::Continue(r2) => { | 
|---|
| 1052 | r = r2; | 
|---|
| 1053 | } | 
|---|
| 1054 | }, | 
|---|
| 1055 | LookupResult::Load { load, continuation } => { | 
|---|
| 1056 | return LookupResult::Load { | 
|---|
| 1057 | load, | 
|---|
| 1058 | continuation: LoopingLookup { | 
|---|
| 1059 | continuation, | 
|---|
| 1060 | mutator, | 
|---|
| 1061 | }, | 
|---|
| 1062 | }; | 
|---|
| 1063 | } | 
|---|
| 1064 | } | 
|---|
| 1065 | } | 
|---|
| 1066 | } | 
|---|
| 1067 | } | 
|---|
| 1068 |  | 
|---|
| 1069 | impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F> | 
|---|
| 1070 | where | 
|---|
| 1071 | L: LookupContinuation, | 
|---|
| 1072 | F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>, | 
|---|
| 1073 | { | 
|---|
| 1074 | type Output = T; | 
|---|
| 1075 | type Buf = L::Buf; | 
|---|
| 1076 |  | 
|---|
| 1077 | fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> { | 
|---|
| 1078 | let r: LookupResult = self.continuation.resume(input:v); | 
|---|
| 1079 | LoopingLookup::new_lookup(r, self.mutator) | 
|---|
| 1080 | } | 
|---|
| 1081 | } | 
|---|
| 1082 |  | 
|---|
| 1083 | impl<R: gimli::Reader> ResUnit<R> { | 
|---|
| 1084 | fn dwarf_and_unit_dwo<'unit, 'ctx: 'unit>( | 
|---|
| 1085 | &'unit self, | 
|---|
| 1086 | ctx: &'ctx Context<R>, | 
|---|
| 1087 | ) -> LookupResult< | 
|---|
| 1088 | SimpleLookup< | 
|---|
| 1089 | Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>, | 
|---|
| 1090 | R, | 
|---|
| 1091 | impl FnOnce( | 
|---|
| 1092 | Option<Arc<gimli::Dwarf<R>>>, | 
|---|
| 1093 | ) | 
|---|
| 1094 | -> Result<(DebugFile, &'unit gimli::Dwarf<R>, &'unit gimli::Unit<R>), Error>, | 
|---|
| 1095 | >, | 
|---|
| 1096 | > { | 
|---|
| 1097 | loop { | 
|---|
| 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), | 
|---|
| 1102 | None => { | 
|---|
| 1103 | let dwo_id = match self.dw_unit.dwo_id { | 
|---|
| 1104 | None => { | 
|---|
| 1105 | self.dwo.borrow_with(|| Ok(None)); | 
|---|
| 1106 | continue; | 
|---|
| 1107 | } | 
|---|
| 1108 | Some(dwo_id) => dwo_id, | 
|---|
| 1109 | }; | 
|---|
| 1110 |  | 
|---|
| 1111 | let comp_dir = self.dw_unit.comp_dir.clone(); | 
|---|
| 1112 |  | 
|---|
| 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)?)) | 
|---|
| 1116 | } else { | 
|---|
| 1117 | Ok(None) | 
|---|
| 1118 | } | 
|---|
| 1119 | }); | 
|---|
| 1120 |  | 
|---|
| 1121 | let path = match dwo_name { | 
|---|
| 1122 | Ok(v) => v, | 
|---|
| 1123 | Err(e) => { | 
|---|
| 1124 | self.dwo.borrow_with(|| Err(e)); | 
|---|
| 1125 | continue; | 
|---|
| 1126 | } | 
|---|
| 1127 | }; | 
|---|
| 1128 |  | 
|---|
| 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, | 
|---|
| 1133 | }; | 
|---|
| 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), | 
|---|
| 1138 | }; | 
|---|
| 1139 |  | 
|---|
| 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)))) | 
|---|
| 1143 | }; | 
|---|
| 1144 |  | 
|---|
| 1145 | return SimpleLookup::new_needs_load( | 
|---|
| 1146 | SplitDwarfLoad { | 
|---|
| 1147 | dwo_id, | 
|---|
| 1148 | comp_dir, | 
|---|
| 1149 | path, | 
|---|
| 1150 | parent: ctx.sections.clone(), | 
|---|
| 1151 | }, | 
|---|
| 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)), | 
|---|
| 1155 | Err(e) => Err(*e), | 
|---|
| 1156 | }, | 
|---|
| 1157 | ); | 
|---|
| 1158 | } | 
|---|
| 1159 | }); | 
|---|
| 1160 | } | 
|---|
| 1161 | } | 
|---|
| 1162 |  | 
|---|
| 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 | 
|---|
| 1165 | // to handle DWOs. | 
|---|
| 1166 | let ilnp = match self.dw_unit.line_program { | 
|---|
| 1167 | Some(ref ilnp) => ilnp, | 
|---|
| 1168 | None => return Ok(None), | 
|---|
| 1169 | }; | 
|---|
| 1170 | self.lines | 
|---|
| 1171 | .borrow_with(|| Lines::parse(&self.dw_unit, ilnp.clone(), sections)) | 
|---|
| 1172 | .as_ref() | 
|---|
| 1173 | .map(Some) | 
|---|
| 1174 | .map_err(Error::clone) | 
|---|
| 1175 | } | 
|---|
| 1176 |  | 
|---|
| 1177 | fn parse_functions_dwarf_and_unit( | 
|---|
| 1178 | &self, | 
|---|
| 1179 | unit: &gimli::Unit<R>, | 
|---|
| 1180 | sections: &gimli::Dwarf<R>, | 
|---|
| 1181 | ) -> Result<&Functions<R>, Error> { | 
|---|
| 1182 | self.funcs | 
|---|
| 1183 | .borrow_with(|| Functions::parse(unit, sections)) | 
|---|
| 1184 | .as_ref() | 
|---|
| 1185 | .map_err(Error::clone) | 
|---|
| 1186 | } | 
|---|
| 1187 |  | 
|---|
| 1188 | fn parse_functions<'unit, 'ctx: 'unit>( | 
|---|
| 1189 | &'unit self, | 
|---|
| 1190 | ctx: &'ctx Context<R>, | 
|---|
| 1191 | ) -> LookupResult<impl LookupContinuation<Output = Result<&'unit Functions<R>, Error>, Buf = R>> | 
|---|
| 1192 | { | 
|---|
| 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) | 
|---|
| 1196 | }) | 
|---|
| 1197 | } | 
|---|
| 1198 | fn parse_inlined_functions<'unit, 'ctx: 'unit>( | 
|---|
| 1199 | &'unit self, | 
|---|
| 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?; | 
|---|
| 1204 | self.funcs | 
|---|
| 1205 | .borrow_with(|| Functions::parse(unit, sections)) | 
|---|
| 1206 | .as_ref() | 
|---|
| 1207 | .map_err(Error::clone)? | 
|---|
| 1208 | .parse_inlined_functions(file, unit, ctx, sections) | 
|---|
| 1209 | }) | 
|---|
| 1210 | } | 
|---|
| 1211 |  | 
|---|
| 1212 | fn find_location( | 
|---|
| 1213 | &self, | 
|---|
| 1214 | probe: u64, | 
|---|
| 1215 | sections: &gimli::Dwarf<R>, | 
|---|
| 1216 | ) -> Result<Option<Location<'_>>, Error> { | 
|---|
| 1217 | if let Some(mut iter) = LocationRangeUnitIter::new(self, sections, probe, probe + 1)? { | 
|---|
| 1218 | match iter.next() { | 
|---|
| 1219 | None => Ok(None), | 
|---|
| 1220 | Some((_addr, _len, loc)) => Ok(Some(loc)), | 
|---|
| 1221 | } | 
|---|
| 1222 | } else { | 
|---|
| 1223 | Ok(None) | 
|---|
| 1224 | } | 
|---|
| 1225 | } | 
|---|
| 1226 |  | 
|---|
| 1227 | #[ inline] | 
|---|
| 1228 | fn find_location_range( | 
|---|
| 1229 | &self, | 
|---|
| 1230 | probe_low: u64, | 
|---|
| 1231 | probe_high: u64, | 
|---|
| 1232 | sections: &gimli::Dwarf<R>, | 
|---|
| 1233 | ) -> Result<Option<LocationRangeUnitIter<'_>>, Error> { | 
|---|
| 1234 | LocationRangeUnitIter::new(self, sections, probe_low, probe_high) | 
|---|
| 1235 | } | 
|---|
| 1236 |  | 
|---|
| 1237 | fn find_function_or_location<'unit, 'ctx: 'unit>( | 
|---|
| 1238 | &'unit self, | 
|---|
| 1239 | probe: u64, | 
|---|
| 1240 | ctx: &'ctx Context<R>, | 
|---|
| 1241 | ) -> LookupResult< | 
|---|
| 1242 | impl LookupContinuation< | 
|---|
| 1243 | Output = Result<(Option<&'unit Function<R>>, Option<Location<'unit>>), Error>, | 
|---|
| 1244 | Buf = R, | 
|---|
| 1245 | >, | 
|---|
| 1246 | > { | 
|---|
| 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) { | 
|---|
| 1251 | Some(address) => { | 
|---|
| 1252 | let function_index = functions.addresses[address].function; | 
|---|
| 1253 | let (offset, ref function) = functions.functions[function_index]; | 
|---|
| 1254 | Some( | 
|---|
| 1255 | function | 
|---|
| 1256 | .borrow_with(|| Function::parse(offset, file, unit, ctx, sections)) | 
|---|
| 1257 | .as_ref() | 
|---|
| 1258 | .map_err(Error::clone)?, | 
|---|
| 1259 | ) | 
|---|
| 1260 | } | 
|---|
| 1261 | None => None, | 
|---|
| 1262 | }; | 
|---|
| 1263 | let location = self.find_location(probe, sections)?; | 
|---|
| 1264 | Ok((function, location)) | 
|---|
| 1265 | }) | 
|---|
| 1266 | } | 
|---|
| 1267 | } | 
|---|
| 1268 |  | 
|---|
| 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>>, | 
|---|
| 1273 |  | 
|---|
| 1274 | probe_low: u64, | 
|---|
| 1275 | probe_high: u64, | 
|---|
| 1276 | sections: &'ctx gimli::Dwarf<R>, | 
|---|
| 1277 | } | 
|---|
| 1278 |  | 
|---|
| 1279 | impl<'ctx, R: gimli::Reader> LocationRangeIter<'ctx, R> { | 
|---|
| 1280 | #[ inline] | 
|---|
| 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); | 
|---|
| 1284 | Ok(Self { | 
|---|
| 1285 | unit_iter: Box::new(unit_iter), | 
|---|
| 1286 | iter: None, | 
|---|
| 1287 | probe_low, | 
|---|
| 1288 | probe_high, | 
|---|
| 1289 | sections, | 
|---|
| 1290 | }) | 
|---|
| 1291 | } | 
|---|
| 1292 |  | 
|---|
| 1293 | fn next_loc(&mut self) -> Result<Option<(u64, u64, Location<'ctx>)>, Error> { | 
|---|
| 1294 | loop { | 
|---|
| 1295 | let iter = self.iter.take(); | 
|---|
| 1296 | match iter { | 
|---|
| 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), | 
|---|
| 1302 | self.sections, | 
|---|
| 1303 | )?; | 
|---|
| 1304 | } | 
|---|
| 1305 | None => return Ok(None), | 
|---|
| 1306 | }, | 
|---|
| 1307 | Some(mut iter) => { | 
|---|
| 1308 | if let item @ Some(_) = iter.next() { | 
|---|
| 1309 | self.iter = Some(iter); | 
|---|
| 1310 | return Ok(item); | 
|---|
| 1311 | } | 
|---|
| 1312 | } | 
|---|
| 1313 | } | 
|---|
| 1314 | } | 
|---|
| 1315 | } | 
|---|
| 1316 | } | 
|---|
| 1317 |  | 
|---|
| 1318 | impl<'ctx, R> Iterator for LocationRangeIter<'ctx, R> | 
|---|
| 1319 | where | 
|---|
| 1320 | R: gimli::Reader + 'ctx, | 
|---|
| 1321 | { | 
|---|
| 1322 | type Item = (u64, u64, Location<'ctx>); | 
|---|
| 1323 |  | 
|---|
| 1324 | #[ inline] | 
|---|
| 1325 | fn next(&mut self) -> Option<Self::Item> { | 
|---|
| 1326 | match self.next_loc() { | 
|---|
| 1327 | Err(_) => None, | 
|---|
| 1328 | Ok(loc: Option<(u64, u64, Location<'ctx>)>) => loc, | 
|---|
| 1329 | } | 
|---|
| 1330 | } | 
|---|
| 1331 | } | 
|---|
| 1332 |  | 
|---|
| 1333 | #[ cfg(feature = "fallible-iterator")] | 
|---|
| 1334 | impl<'ctx, R> fallible_iterator::FallibleIterator for LocationRangeIter<'ctx, R> | 
|---|
| 1335 | where | 
|---|
| 1336 | R: gimli::Reader + 'ctx, | 
|---|
| 1337 | { | 
|---|
| 1338 | type Item = (u64, u64, Location<'ctx>); | 
|---|
| 1339 | type Error = Error; | 
|---|
| 1340 |  | 
|---|
| 1341 | #[ inline] | 
|---|
| 1342 | fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> { | 
|---|
| 1343 | self.next_loc() | 
|---|
| 1344 | } | 
|---|
| 1345 | } | 
|---|
| 1346 |  | 
|---|
| 1347 | struct LocationRangeUnitIter<'ctx> { | 
|---|
| 1348 | lines: &'ctx Lines, | 
|---|
| 1349 | seqs: &'ctx [LineSequence], | 
|---|
| 1350 | seq_idx: usize, | 
|---|
| 1351 | row_idx: usize, | 
|---|
| 1352 | probe_high: u64, | 
|---|
| 1353 | } | 
|---|
| 1354 |  | 
|---|
| 1355 | impl<'ctx> LocationRangeUnitIter<'ctx> { | 
|---|
| 1356 | fn new<R: gimli::Reader>( | 
|---|
| 1357 | resunit: &'ctx ResUnit<R>, | 
|---|
| 1358 | sections: &gimli::Dwarf<R>, | 
|---|
| 1359 | probe_low: u64, | 
|---|
| 1360 | probe_high: u64, | 
|---|
| 1361 | ) -> Result<Option<Self>, Error> { | 
|---|
| 1362 | let lines = resunit.parse_lines(sections)?; | 
|---|
| 1363 |  | 
|---|
| 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 { | 
|---|
| 1368 | Ordering::Greater | 
|---|
| 1369 | } else if probe_low >= sequence.end { | 
|---|
| 1370 | Ordering::Less | 
|---|
| 1371 | } else { | 
|---|
| 1372 | Ordering::Equal | 
|---|
| 1373 | } | 
|---|
| 1374 | }); | 
|---|
| 1375 | let seq_idx = match seq_idx { | 
|---|
| 1376 | Ok(x) => x, | 
|---|
| 1377 | Err(0) => 0, // probe below sequence, but range could overlap | 
|---|
| 1378 | Err(_) => lines.sequences.len(), | 
|---|
| 1379 | }; | 
|---|
| 1380 |  | 
|---|
| 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)); | 
|---|
| 1383 | match idx { | 
|---|
| 1384 | Ok(x) => x, | 
|---|
| 1385 | Err(0) => 0, // probe below sequence, but range could overlap | 
|---|
| 1386 | Err(x) => x - 1, | 
|---|
| 1387 | } | 
|---|
| 1388 | } else { | 
|---|
| 1389 | 0 | 
|---|
| 1390 | }; | 
|---|
| 1391 |  | 
|---|
| 1392 | Ok(Some(Self { | 
|---|
| 1393 | lines, | 
|---|
| 1394 | seqs: &*lines.sequences, | 
|---|
| 1395 | seq_idx, | 
|---|
| 1396 | row_idx, | 
|---|
| 1397 | probe_high, | 
|---|
| 1398 | })) | 
|---|
| 1399 | } else { | 
|---|
| 1400 | Ok(None) | 
|---|
| 1401 | } | 
|---|
| 1402 | } | 
|---|
| 1403 | } | 
|---|
| 1404 |  | 
|---|
| 1405 | impl<'ctx> Iterator for LocationRangeUnitIter<'ctx> { | 
|---|
| 1406 | type Item = (u64, u64, Location<'ctx>); | 
|---|
| 1407 |  | 
|---|
| 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 { | 
|---|
| 1411 | break; | 
|---|
| 1412 | } | 
|---|
| 1413 |  | 
|---|
| 1414 | match seq.rows.get(self.row_idx) { | 
|---|
| 1415 | Some(row) => { | 
|---|
| 1416 | if row.address >= self.probe_high { | 
|---|
| 1417 | break; | 
|---|
| 1418 | } | 
|---|
| 1419 |  | 
|---|
| 1420 | let file = self | 
|---|
| 1421 | .lines | 
|---|
| 1422 | .files | 
|---|
| 1423 | .get(row.file_index as usize) | 
|---|
| 1424 | .map(String::as_str); | 
|---|
| 1425 | let nextaddr = seq | 
|---|
| 1426 | .rows | 
|---|
| 1427 | .get(self.row_idx + 1) | 
|---|
| 1428 | .map(|row| row.address) | 
|---|
| 1429 | .unwrap_or(seq.end); | 
|---|
| 1430 |  | 
|---|
| 1431 | let item = ( | 
|---|
| 1432 | row.address, | 
|---|
| 1433 | nextaddr - row.address, | 
|---|
| 1434 | Location { | 
|---|
| 1435 | file, | 
|---|
| 1436 | line: if row.line != 0 { Some(row.line) } else { None }, | 
|---|
| 1437 | column: if row.column != 0 { | 
|---|
| 1438 | Some(row.column) | 
|---|
| 1439 | } else { | 
|---|
| 1440 | None | 
|---|
| 1441 | }, | 
|---|
| 1442 | }, | 
|---|
| 1443 | ); | 
|---|
| 1444 | self.row_idx += 1; | 
|---|
| 1445 |  | 
|---|
| 1446 | return Some(item); | 
|---|
| 1447 | } | 
|---|
| 1448 | None => { | 
|---|
| 1449 | self.seq_idx += 1; | 
|---|
| 1450 | self.row_idx = 0; | 
|---|
| 1451 | } | 
|---|
| 1452 | } | 
|---|
| 1453 | } | 
|---|
| 1454 | None | 
|---|
| 1455 | } | 
|---|
| 1456 | } | 
|---|
| 1457 |  | 
|---|
| 1458 | fn path_push(path: &mut String, p: &str) { | 
|---|
| 1459 | if has_unix_root(p) || has_windows_root(p) { | 
|---|
| 1460 | *path = p.to_string(); | 
|---|
| 1461 | } else { | 
|---|
| 1462 | let dir_separator: char = if has_windows_root(path.as_str()) { | 
|---|
| 1463 | '\\ ' | 
|---|
| 1464 | } else { | 
|---|
| 1465 | '/' | 
|---|
| 1466 | }; | 
|---|
| 1467 |  | 
|---|
| 1468 | if !path.is_empty() && !path.ends_with(dir_separator) { | 
|---|
| 1469 | path.push(ch:dir_separator); | 
|---|
| 1470 | } | 
|---|
| 1471 | *path += p; | 
|---|
| 1472 | } | 
|---|
| 1473 | } | 
|---|
| 1474 |  | 
|---|
| 1475 | /// Check if the path in the given string has a unix style root | 
|---|
| 1476 | fn has_unix_root(p: &str) -> bool { | 
|---|
| 1477 | p.starts_with( '/') | 
|---|
| 1478 | } | 
|---|
| 1479 |  | 
|---|
| 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( ":\\ ") | 
|---|
| 1483 | } | 
|---|
| 1484 | struct RangeAttributes<R: gimli::Reader> { | 
|---|
| 1485 | low_pc: Option<u64>, | 
|---|
| 1486 | high_pc: Option<u64>, | 
|---|
| 1487 | size: Option<u64>, | 
|---|
| 1488 | ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>, | 
|---|
| 1489 | } | 
|---|
| 1490 |  | 
|---|
| 1491 | impl<R: gimli::Reader> Default for RangeAttributes<R> { | 
|---|
| 1492 | fn default() -> Self { | 
|---|
| 1493 | RangeAttributes { | 
|---|
| 1494 | low_pc: None, | 
|---|
| 1495 | high_pc: None, | 
|---|
| 1496 | size: None, | 
|---|
| 1497 | ranges_offset: None, | 
|---|
| 1498 | } | 
|---|
| 1499 | } | 
|---|
| 1500 | } | 
|---|
| 1501 |  | 
|---|
| 1502 | impl<R: gimli::Reader> RangeAttributes<R> { | 
|---|
| 1503 | fn for_each_range<F: FnMut(gimli::Range)>( | 
|---|
| 1504 | &self, | 
|---|
| 1505 | sections: &gimli::Dwarf<R>, | 
|---|
| 1506 | unit: &gimli::Unit<R>, | 
|---|
| 1507 | mut f: F, | 
|---|
| 1508 | ) -> Result<bool, Error> { | 
|---|
| 1509 | let mut added_any = false; | 
|---|
| 1510 | let mut add_range = |range: gimli::Range| { | 
|---|
| 1511 | if range.begin < range.end { | 
|---|
| 1512 | f(range); | 
|---|
| 1513 | added_any = true | 
|---|
| 1514 | } | 
|---|
| 1515 | }; | 
|---|
| 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()? { | 
|---|
| 1519 | add_range(range); | 
|---|
| 1520 | } | 
|---|
| 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 { | 
|---|
| 1525 | begin, | 
|---|
| 1526 | end: begin + size, | 
|---|
| 1527 | }); | 
|---|
| 1528 | } | 
|---|
| 1529 | Ok(added_any) | 
|---|
| 1530 | } | 
|---|
| 1531 | } | 
|---|
| 1532 |  | 
|---|
| 1533 | /// An iterator over function frames. | 
|---|
| 1534 | pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>) | 
|---|
| 1535 | where | 
|---|
| 1536 | R: gimli::Reader; | 
|---|
| 1537 |  | 
|---|
| 1538 | enum FrameIterState<'ctx, R> | 
|---|
| 1539 | where | 
|---|
| 1540 | R: gimli::Reader, | 
|---|
| 1541 | { | 
|---|
| 1542 | Empty, | 
|---|
| 1543 | Location(Option<Location<'ctx>>), | 
|---|
| 1544 | Frames(FrameIterFrames<'ctx, R>), | 
|---|
| 1545 | } | 
|---|
| 1546 |  | 
|---|
| 1547 | struct FrameIterFrames<'ctx, R> | 
|---|
| 1548 | where | 
|---|
| 1549 | R: gimli::Reader, | 
|---|
| 1550 | { | 
|---|
| 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>>, | 
|---|
| 1556 | } | 
|---|
| 1557 |  | 
|---|
| 1558 | impl<'ctx, R> FrameIter<'ctx, R> | 
|---|
| 1559 | where | 
|---|
| 1560 | R: gimli::Reader + 'ctx, | 
|---|
| 1561 | { | 
|---|
| 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, | 
|---|
| 1572 | function: None, | 
|---|
| 1573 | location, | 
|---|
| 1574 | })); | 
|---|
| 1575 | } | 
|---|
| 1576 | FrameIterState::Frames(frames) => frames, | 
|---|
| 1577 | }; | 
|---|
| 1578 |  | 
|---|
| 1579 | let loc = frames.next.take(); | 
|---|
| 1580 | let func = match frames.inlined_functions.next() { | 
|---|
| 1581 | Some(func) => func, | 
|---|
| 1582 | None => { | 
|---|
| 1583 | let frame = Frame { | 
|---|
| 1584 | dw_die_offset: Some(frames.function.dw_die_offset), | 
|---|
| 1585 | function: frames.function.name.clone().map(|name| FunctionName { | 
|---|
| 1586 | name, | 
|---|
| 1587 | language: frames.unit.lang, | 
|---|
| 1588 | }), | 
|---|
| 1589 | location: loc, | 
|---|
| 1590 | }; | 
|---|
| 1591 | self.0 = FrameIterState::Empty; | 
|---|
| 1592 | return Ok(Some(frame)); | 
|---|
| 1593 | } | 
|---|
| 1594 | }; | 
|---|
| 1595 |  | 
|---|
| 1596 | let mut next = Location { | 
|---|
| 1597 | file: None, | 
|---|
| 1598 | line: if func.call_line != 0 { | 
|---|
| 1599 | Some(func.call_line) | 
|---|
| 1600 | } else { | 
|---|
| 1601 | None | 
|---|
| 1602 | }, | 
|---|
| 1603 | column: if func.call_column != 0 { | 
|---|
| 1604 | Some(func.call_column) | 
|---|
| 1605 | } else { | 
|---|
| 1606 | None | 
|---|
| 1607 | }, | 
|---|
| 1608 | }; | 
|---|
| 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); | 
|---|
| 1612 | } | 
|---|
| 1613 | } | 
|---|
| 1614 | frames.next = Some(next); | 
|---|
| 1615 |  | 
|---|
| 1616 | Ok(Some(Frame { | 
|---|
| 1617 | dw_die_offset: Some(func.dw_die_offset), | 
|---|
| 1618 | function: func.name.clone().map(|name| FunctionName { | 
|---|
| 1619 | name, | 
|---|
| 1620 | language: frames.unit.lang, | 
|---|
| 1621 | }), | 
|---|
| 1622 | location: loc, | 
|---|
| 1623 | })) | 
|---|
| 1624 | } | 
|---|
| 1625 | } | 
|---|
| 1626 |  | 
|---|
| 1627 | #[ cfg(feature = "fallible-iterator")] | 
|---|
| 1628 | impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R> | 
|---|
| 1629 | where | 
|---|
| 1630 | R: gimli::Reader + 'ctx, | 
|---|
| 1631 | { | 
|---|
| 1632 | type Item = Frame<'ctx, R>; | 
|---|
| 1633 | type Error = Error; | 
|---|
| 1634 |  | 
|---|
| 1635 | #[ inline] | 
|---|
| 1636 | fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> { | 
|---|
| 1637 | self.next() | 
|---|
| 1638 | } | 
|---|
| 1639 | } | 
|---|
| 1640 |  | 
|---|
| 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>>, | 
|---|
| 1649 | } | 
|---|
| 1650 |  | 
|---|
| 1651 | /// A function name. | 
|---|
| 1652 | pub struct FunctionName<R: gimli::Reader> { | 
|---|
| 1653 | /// The name of the function. | 
|---|
| 1654 | pub name: R, | 
|---|
| 1655 | /// The language of the compilation unit containing this function. | 
|---|
| 1656 | pub language: Option<gimli::DwLang>, | 
|---|
| 1657 | } | 
|---|
| 1658 |  | 
|---|
| 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() | 
|---|
| 1663 | } | 
|---|
| 1664 |  | 
|---|
| 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: Cow<'_, str>| demangle_auto(name:x, self.language)) | 
|---|
| 1668 | } | 
|---|
| 1669 | } | 
|---|
| 1670 |  | 
|---|
| 1671 | /// Demangle a symbol name using the demangling scheme for the given language. | 
|---|
| 1672 | /// | 
|---|
| 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> { | 
|---|
| 1676 | match language { | 
|---|
| 1677 | #[ cfg(feature = "rustc-demangle")] | 
|---|
| 1678 | gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name) | 
|---|
| 1679 | .ok() | 
|---|
| 1680 | .as_ref() | 
|---|
| 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) | 
|---|
| 1687 | .ok() | 
|---|
| 1688 | .and_then(|x| x.demangle(&Default::default()).ok()), | 
|---|
| 1689 | _ => None, | 
|---|
| 1690 | } | 
|---|
| 1691 | } | 
|---|
| 1692 |  | 
|---|
| 1693 | /// Apply 'best effort' demangling of a symbol name. | 
|---|
| 1694 | /// | 
|---|
| 1695 | /// If `language` is given, then only the demangling scheme for that language | 
|---|
| 1696 | /// is used. | 
|---|
| 1697 | /// | 
|---|
| 1698 | /// If `language` is `None`, then heuristics are used to determine how to | 
|---|
| 1699 | /// demangle the name. Currently, these heuristics are very basic. | 
|---|
| 1700 | /// | 
|---|
| 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> { | 
|---|
| 1703 | match language { | 
|---|
| 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)), | 
|---|
| 1707 | } | 
|---|
| 1708 | .map(Cow::from) | 
|---|
| 1709 | .unwrap_or(default:name) | 
|---|
| 1710 | } | 
|---|
| 1711 |  | 
|---|
| 1712 | /// A source location. | 
|---|
| 1713 | pub struct Location<'a> { | 
|---|
| 1714 | /// The file name. | 
|---|
| 1715 | pub file: Option<&'a str>, | 
|---|
| 1716 | /// The line number. | 
|---|
| 1717 | pub line: Option<u32>, | 
|---|
| 1718 | /// The column number. | 
|---|
| 1719 | pub column: Option<u32>, | 
|---|
| 1720 | } | 
|---|
| 1721 |  | 
|---|
| 1722 | #[ cfg(test)] | 
|---|
| 1723 | mod tests { | 
|---|
| 1724 | #[ test] | 
|---|
| 1725 | fn context_is_send() { | 
|---|
| 1726 | fn assert_is_send<T: Send>() {} | 
|---|
| 1727 | assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>(); | 
|---|
| 1728 | } | 
|---|
| 1729 | } | 
|---|
| 1730 |  | 
|---|