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