]>
Commit | Line | Data |
---|---|---|
9ffffee4 FG |
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 | ||
fe692bf9 FG |
28 | #[cfg(feature = "std")] |
29 | extern crate std; | |
30 | ||
9ffffee4 FG |
31 | #[allow(unused_imports)] |
32 | #[macro_use] | |
33 | extern crate alloc; | |
34 | ||
9ffffee4 FG |
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; | |
9ffffee4 FG |
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; | |
fe692bf9 | 51 | use core::marker::PhantomData; |
9ffffee4 FG |
52 | use core::mem; |
53 | use core::num::NonZeroU64; | |
fe692bf9 | 54 | use core::ops::ControlFlow; |
9ffffee4 FG |
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 | ||
fe692bf9 FG |
71 | #[cfg(all(feature = "std", feature = "object", feature = "memmap2"))] |
72 | /// A simple builtin split DWARF loader. | |
73 | pub mod builtin_split_dwarf_loader; | |
9ffffee4 FG |
74 | mod function; |
75 | mod lazy; | |
76 | ||
77 | type Error = gimli::Error; | |
78 | ||
fe692bf9 FG |
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 | ||
9ffffee4 FG |
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> { | |
fe692bf9 FG |
185 | sections: Arc<gimli::Dwarf<R>>, |
186 | unit_ranges: Vec<UnitRange>, | |
187 | units: Vec<ResUnit<R>>, | |
188 | sup_units: Vec<SupUnit<R>>, | |
9ffffee4 FG |
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 | /// | |
fe692bf9 | 220 | /// Performance sensitive applications may want to use `Context::from_dwarf` |
9ffffee4 FG |
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(), | |
fe692bf9 | 287 | default_section.into(), |
9ffffee4 FG |
288 | ), |
289 | ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists), | |
290 | file_type: gimli::DwarfFileType::Main, | |
291 | sup: None, | |
fe692bf9 | 292 | abbreviations_cache: gimli::AbbreviationsCache::new(), |
9ffffee4 FG |
293 | }) |
294 | } | |
295 | ||
296 | /// Construct a new `Context` from an existing [`gimli::Dwarf`] object. | |
297 | #[inline] | |
fe692bf9 FG |
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() | |
9ffffee4 | 305 | }; |
fe692bf9 FG |
306 | Ok(Context { |
307 | sections, | |
308 | unit_ranges, | |
309 | units, | |
310 | sup_units, | |
311 | }) | |
9ffffee4 FG |
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 | |
9ffffee4 FG |
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. | |
fe692bf9 | 356 | self.unit_ranges[..pos] |
9ffffee4 FG |
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 | } | |
fe692bf9 | 377 | Some((&self.units[i.unit_id], &i.range)) |
9ffffee4 FG |
378 | }) |
379 | } | |
380 | ||
381 | /// Find the DWARF unit corresponding to the given virtual memory address. | |
fe692bf9 FG |
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 | ); | |
9ffffee4 | 413 | } |
fe692bf9 FG |
414 | |
415 | LoopingLookup::new_complete(None) | |
9ffffee4 FG |
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) { | |
fe692bf9 | 421 | if let Some(location) = unit.find_location(probe, &self.sections)? { |
9ffffee4 FG |
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. | |
fe692bf9 FG |
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))) | |
9ffffee4 | 482 | } |
fe692bf9 FG |
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 | }) | |
9ffffee4 FG |
529 | } |
530 | ||
531 | /// Initialize all line data structures. This is used for benchmarks. | |
532 | #[doc(hidden)] | |
533 | pub fn parse_lines(&self) -> Result<(), Error> { | |
fe692bf9 FG |
534 | for unit in &self.units { |
535 | unit.parse_lines(&self.sections)?; | |
9ffffee4 FG |
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> { | |
fe692bf9 FG |
543 | for unit in &self.units { |
544 | unit.parse_functions(self).skip_all_loads()?; | |
9ffffee4 FG |
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> { | |
fe692bf9 FG |
552 | for unit in &self.units { |
553 | unit.parse_inlined_functions(self).skip_all_loads()?; | |
9ffffee4 FG |
554 | } |
555 | Ok(()) | |
556 | } | |
557 | } | |
558 | ||
559 | struct UnitRange { | |
560 | unit_id: usize, | |
561 | max_end: u64, | |
562 | range: gimli::Range, | |
563 | } | |
564 | ||
fe692bf9 FG |
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>>, | |
9ffffee4 FG |
572 | } |
573 | ||
fe692bf9 FG |
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> { | |
9ffffee4 FG |
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; | |
49aad941 | 612 | let mut have_unit_range = false; |
9ffffee4 FG |
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() { | |
49aad941 FG |
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)?); | |
9ffffee4 | 629 | } |
49aad941 FG |
630 | _ => {} |
631 | }, | |
9ffffee4 FG |
632 | gimli::DW_AT_high_pc => match attr.value() { |
633 | gimli::AttributeValue::Addr(val) => ranges.high_pc = Some(val), | |
49aad941 FG |
634 | gimli::AttributeValue::DebugAddrIndex(index) => { |
635 | ranges.high_pc = Some(sections.address(&dw_unit, index)?); | |
636 | } | |
9ffffee4 FG |
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 | }); | |
49aad941 | 686 | have_unit_range = true; |
9ffffee4 FG |
687 | } |
688 | } | |
689 | } | |
690 | } else { | |
fe692bf9 | 691 | have_unit_range |= ranges.for_each_range(sections, &dw_unit, |range| { |
9ffffee4 FG |
692 | unit_ranges.push(UnitRange { |
693 | range, | |
694 | unit_id, | |
695 | max_end: 0, | |
696 | }); | |
697 | })?; | |
698 | } | |
699 | } | |
700 | ||
49aad941 FG |
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 | |
fe692bf9 | 707 | .borrow_with(|| Lines::parse(&dw_unit, ilnp.clone(), sections)) |
49aad941 FG |
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 | ||
9ffffee4 FG |
724 | res_units.push(ResUnit { |
725 | offset, | |
726 | dw_unit, | |
727 | lang, | |
49aad941 | 728 | lines, |
9ffffee4 | 729 | funcs: LazyCell::new(), |
fe692bf9 | 730 | dwo: LazyCell::new(), |
9ffffee4 FG |
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 | ||
fe692bf9 | 745 | Ok((unit_ranges, res_units)) |
9ffffee4 FG |
746 | } |
747 | ||
fe692bf9 FG |
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 }); | |
9ffffee4 | 761 | } |
fe692bf9 FG |
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)) | |
9ffffee4 FG |
799 | } |
800 | } | |
801 | ||
802 | struct Lines { | |
803 | files: Box<[String]>, | |
804 | sequences: Box<[LineSequence]>, | |
805 | } | |
806 | ||
49aad941 FG |
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 | ||
9ffffee4 FG |
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 | ||
fe692bf9 FG |
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(v) { | |
1002 | LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)), | |
1003 | LookupResult::Load { load, continuation } => 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 = self.continuation.resume(v); | |
1079 | LoopingLookup::new_lookup(r, self.mutator) | |
1080 | } | |
9ffffee4 FG |
1081 | } |
1082 | ||
1083 | impl<R: gimli::Reader> ResUnit<R> { | |
fe692bf9 FG |
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 | ||
9ffffee4 | 1163 | fn parse_lines(&self, sections: &gimli::Dwarf<R>) -> Result<Option<&Lines>, Error> { |
fe692bf9 FG |
1164 | // NB: line information is always stored in the main debug file so this does not need |
1165 | // to handle DWOs. | |
9ffffee4 FG |
1166 | let ilnp = match self.dw_unit.line_program { |
1167 | Some(ref ilnp) => ilnp, | |
1168 | None => return Ok(None), | |
1169 | }; | |
1170 | self.lines | |
49aad941 | 1171 | .borrow_with(|| Lines::parse(&self.dw_unit, ilnp.clone(), sections)) |
9ffffee4 FG |
1172 | .as_ref() |
1173 | .map(Some) | |
1174 | .map_err(Error::clone) | |
1175 | } | |
1176 | ||
fe692bf9 FG |
1177 | fn parse_functions_dwarf_and_unit( |
1178 | &self, | |
1179 | unit: &gimli::Unit<R>, | |
1180 | sections: &gimli::Dwarf<R>, | |
1181 | ) -> Result<&Functions<R>, Error> { | |
9ffffee4 | 1182 | self.funcs |
fe692bf9 | 1183 | .borrow_with(|| Functions::parse(unit, sections)) |
9ffffee4 FG |
1184 | .as_ref() |
1185 | .map_err(Error::clone) | |
1186 | } | |
1187 | ||
fe692bf9 FG |
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 | }) | |
9ffffee4 FG |
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 | ||
fe692bf9 FG |
1237 | fn find_function_or_location<'unit, 'ctx: 'unit>( |
1238 | &'unit self, | |
9ffffee4 | 1239 | probe: u64, |
fe692bf9 FG |
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 | }) | |
9ffffee4 | 1266 | } |
9ffffee4 FG |
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> { | |
fe692bf9 | 1282 | let sections = &ctx.sections; |
9ffffee4 FG |
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) => 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)); | |
fe692bf9 | 1383 | match idx { |
9ffffee4 FG |
1384 | Ok(x) => x, |
1385 | Err(0) => 0, // probe below sequence, but range could overlap | |
1386 | Err(x) => x - 1, | |
fe692bf9 | 1387 | } |
9ffffee4 FG |
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>)> { | |
fe692bf9 | 1409 | while let Some(seq) = self.seqs.get(self.seq_idx) { |
9ffffee4 FG |
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 = if has_windows_root(path.as_str()) { | |
1463 | '\\' | |
1464 | } else { | |
1465 | '/' | |
1466 | }; | |
1467 | ||
49aad941 | 1468 | if !path.is_empty() && !path.ends_with(dir_separator) { |
9ffffee4 FG |
1469 | path.push(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 | |
fe692bf9 | 1536 | R: gimli::Reader; |
9ffffee4 FG |
1537 | |
1538 | enum FrameIterState<'ctx, R> | |
1539 | where | |
fe692bf9 | 1540 | R: gimli::Reader, |
9ffffee4 FG |
1541 | { |
1542 | Empty, | |
1543 | Location(Option<Location<'ctx>>), | |
1544 | Frames(FrameIterFrames<'ctx, R>), | |
1545 | } | |
1546 | ||
1547 | struct FrameIterFrames<'ctx, R> | |
1548 | where | |
fe692bf9 | 1549 | R: gimli::Reader, |
9ffffee4 FG |
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 | }; | |
fe692bf9 | 1609 | if let Some(call_file) = func.call_file { |
9ffffee4 | 1610 | if let Some(lines) = frames.unit.parse_lines(frames.sections)? { |
fe692bf9 | 1611 | next.file = lines.files.get(call_file as usize).map(String::as_str); |
9ffffee4 FG |
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. | |
fe692bf9 | 1661 | pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> { |
9ffffee4 FG |
1662 | self.name.to_string_lossy() |
1663 | } | |
1664 | ||
1665 | /// The name of this function after demangling (if applicable). | |
fe692bf9 | 1666 | pub fn demangle(&self) -> Result<Cow<'_, str>, Error> { |
9ffffee4 FG |
1667 | self.raw_name().map(|x| demangle_auto(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. | |
fe692bf9 | 1702 | pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> { |
9ffffee4 FG |
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(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>() {} | |
fe692bf9 | 1727 | assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>(); |
9ffffee4 FG |
1728 | } |
1729 | } |