1#![deny(missing_docs)]
28#![no_std]
29
30#[cfg(feature = "cargo-all")]
31compile_error!("'--all-features' is not supported; use '--features all' instead");
32
33#[cfg(feature = "std")]
34extern crate std;
35
36#[allow(unused_imports)]
37#[macro_use]
38extern crate alloc;
39
40#[cfg(feature = "fallible-iterator")]
41pub extern crate fallible_iterator;
42pub extern crate gimli;
43
44use alloc::sync::Arc;
45use core::ops::ControlFlow;
46
47use crate::function::{Function, Functions, InlinedFunction, LazyFunctions};
48use crate::line::{LazyLines, LineLocationRangeIter, Lines};
49use crate::lookup::{LoopingLookup, SimpleLookup};
50use crate::unit::{ResUnit, ResUnits, SupUnits};
51
52#[cfg(feature = "smallvec")]
53mod maybe_small {
54 pub type Vec<T> = smallvec::SmallVec<[T; 16]>;
55 pub type IntoIter<T> = smallvec::IntoIter<[T; 16]>;
56}
57#[cfg(not(feature = "smallvec"))]
58mod maybe_small {
59 pub type Vec<T> = alloc::vec::Vec<T>;
60 pub type IntoIter<T> = alloc::vec::IntoIter<T>;
61}
62
63mod frame;
64pub use frame::{demangle, demangle_auto, Frame, FrameIter, FunctionName, Location};
65
66mod function;
67mod lazy;
68mod line;
69
70#[cfg(feature = "loader")]
71mod loader;
72#[cfg(feature = "loader")]
73pub use loader::{Loader, LoaderReader};
74
75mod lookup;
76pub use lookup::{LookupContinuation, LookupResult, SplitDwarfLoad};
77
78mod unit;
79pub use unit::LocationRangeIter;
80
81type Error = gimli::Error;
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84enum DebugFile {
85 Primary,
86 Supplementary,
87 Dwo,
88}
89
90pub struct Context<R: gimli::Reader> {
95 sections: Arc<gimli::Dwarf<R>>,
96 units: ResUnits<R>,
97 sup_units: SupUnits<R>,
98}
99
100impl<R: gimli::Reader> Context<R> {
101 #[allow(clippy::too_many_arguments)]
105 pub fn from_sections(
106 debug_abbrev: gimli::DebugAbbrev<R>,
107 debug_addr: gimli::DebugAddr<R>,
108 debug_aranges: gimli::DebugAranges<R>,
109 debug_info: gimli::DebugInfo<R>,
110 debug_line: gimli::DebugLine<R>,
111 debug_line_str: gimli::DebugLineStr<R>,
112 debug_ranges: gimli::DebugRanges<R>,
113 debug_rnglists: gimli::DebugRngLists<R>,
114 debug_str: gimli::DebugStr<R>,
115 debug_str_offsets: gimli::DebugStrOffsets<R>,
116 default_section: R,
117 ) -> Result<Self, Error> {
118 Self::from_dwarf(gimli::Dwarf {
119 debug_abbrev,
120 debug_addr,
121 debug_aranges,
122 debug_info,
123 debug_line,
124 debug_line_str,
125 debug_str,
126 debug_str_offsets,
127 debug_types: default_section.clone().into(),
128 locations: gimli::LocationLists::new(
129 default_section.clone().into(),
130 default_section.into(),
131 ),
132 ranges: gimli::RangeLists::new(debug_ranges, debug_rnglists),
133 file_type: gimli::DwarfFileType::Main,
134 sup: None,
135 abbreviations_cache: gimli::AbbreviationsCache::new(),
136 })
137 }
138
139 #[inline]
141 pub fn from_dwarf(sections: gimli::Dwarf<R>) -> Result<Context<R>, Error> {
142 Self::from_arc_dwarf(Arc::new(sections))
143 }
144
145 #[inline]
147 pub fn from_arc_dwarf(sections: Arc<gimli::Dwarf<R>>) -> Result<Context<R>, Error> {
148 let units = ResUnits::parse(§ions)?;
149 let sup_units = if let Some(sup) = sections.sup.as_ref() {
150 SupUnits::parse(sup)?
151 } else {
152 SupUnits::default()
153 };
154 Ok(Context {
155 sections,
156 units,
157 sup_units,
158 })
159 }
160}
161
162impl<R: gimli::Reader> Context<R> {
163 pub fn find_dwarf_and_unit(
165 &self,
166 probe: u64,
167 ) -> LookupResult<impl LookupContinuation<Output = Option<gimli::UnitRef<R>>, Buf = R>> {
168 let mut units_iter = self.units.find(probe);
169 if let Some(unit) = units_iter.next() {
170 return LoopingLookup::new_lookup(
171 unit.find_function_or_location(probe, self),
172 move |r| {
173 ControlFlow::Break(match r {
174 Ok((Some(_), _)) | Ok((_, Some(_))) => {
175 let (_file, unit) = unit
176 .dwarf_and_unit(self)
177 .unwrap()
179 .unwrap();
180 Some(unit)
181 }
182 _ => match units_iter.next() {
183 Some(next_unit) => {
184 return ControlFlow::Continue(
185 next_unit.find_function_or_location(probe, self),
186 );
187 }
188 None => None,
189 },
190 })
191 },
192 );
193 }
194
195 LoopingLookup::new_complete(None)
196 }
197
198 pub fn find_location(&self, probe: u64) -> Result<Option<Location<'_>>, Error> {
200 for unit in self.units.find(probe) {
201 if let Some(location) = unit.find_location(probe, &self.sections)? {
202 return Ok(Some(location));
203 }
204 }
205 Ok(None)
206 }
207
208 pub fn find_location_range(
211 &self,
212 probe_low: u64,
213 probe_high: u64,
214 ) -> Result<LocationRangeIter<'_, R>, Error> {
215 self.units
216 .find_location_range(probe_low, probe_high, &self.sections)
217 }
218
219 pub fn find_frames(
229 &self,
230 probe: u64,
231 ) -> LookupResult<impl LookupContinuation<Output = Result<FrameIter<'_, R>, Error>, Buf = R>>
232 {
233 let mut units_iter = self.units.find(probe);
234 if let Some(unit) = units_iter.next() {
235 LoopingLookup::new_lookup(unit.find_function_or_location(probe, self), move |r| {
236 ControlFlow::Break(match r {
237 Err(e) => Err(e),
238 Ok((Some(function), location)) => {
239 let inlined_functions = function.find_inlined_functions(probe);
240 Ok(FrameIter::new_frames(
241 unit,
242 &self.sections,
243 function,
244 inlined_functions,
245 location,
246 ))
247 }
248 Ok((None, Some(location))) => Ok(FrameIter::new_location(location)),
249 Ok((None, None)) => match units_iter.next() {
250 Some(next_unit) => {
251 return ControlFlow::Continue(
252 next_unit.find_function_or_location(probe, self),
253 );
254 }
255 None => Ok(FrameIter::new_empty()),
256 },
257 })
258 })
259 } else {
260 LoopingLookup::new_complete(Ok(FrameIter::new_empty()))
261 }
262 }
263
264 pub fn preload_units(
293 &'_ self,
294 probe: u64,
295 ) -> impl Iterator<
296 Item = (
297 SplitDwarfLoad<R>,
298 impl FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> Result<(), gimli::Error> + '_,
299 ),
300 > {
301 self.units
302 .find(probe)
303 .filter_map(move |unit| match unit.dwarf_and_unit(self) {
304 LookupResult::Output(_) => None,
305 LookupResult::Load { load, continuation } => Some((load, |result| {
306 continuation.resume(result).unwrap().map(|_| ())
307 })),
308 })
309 }
310
311 #[doc(hidden)]
313 pub fn parse_lines(&self) -> Result<(), Error> {
314 for unit in self.units.iter() {
315 unit.parse_lines(&self.sections)?;
316 }
317 Ok(())
318 }
319
320 #[doc(hidden)]
322 pub fn parse_functions(&self) -> Result<(), Error> {
323 for unit in self.units.iter() {
324 unit.parse_functions(self).skip_all_loads()?;
325 }
326 Ok(())
327 }
328
329 #[doc(hidden)]
331 pub fn parse_inlined_functions(&self) -> Result<(), Error> {
332 for unit in self.units.iter() {
333 unit.parse_inlined_functions(self).skip_all_loads()?;
334 }
335 Ok(())
336 }
337}
338
339impl<R: gimli::Reader> Context<R> {
340 fn find_unit(
342 &self,
343 offset: gimli::DebugInfoOffset<R::Offset>,
344 file: DebugFile,
345 ) -> Result<(&gimli::Unit<R>, gimli::UnitOffset<R::Offset>), Error> {
346 let unit = match file {
347 DebugFile::Primary => self.units.find_offset(offset)?,
348 DebugFile::Supplementary => self.sup_units.find_offset(offset)?,
349 DebugFile::Dwo => return Err(gimli::Error::NoEntryAtGivenOffset),
350 };
351
352 let unit_offset = offset
353 .to_unit_offset(&unit.header)
354 .ok_or(gimli::Error::NoEntryAtGivenOffset)?;
355 Ok((unit, unit_offset))
356 }
357}
358
359struct RangeAttributes<R: gimli::Reader> {
360 low_pc: Option<u64>,
361 high_pc: Option<u64>,
362 size: Option<u64>,
363 ranges_offset: Option<gimli::RangeListsOffset<<R as gimli::Reader>::Offset>>,
364}
365
366impl<R: gimli::Reader> Default for RangeAttributes<R> {
367 fn default() -> Self {
368 RangeAttributes {
369 low_pc: None,
370 high_pc: None,
371 size: None,
372 ranges_offset: None,
373 }
374 }
375}
376
377impl<R: gimli::Reader> RangeAttributes<R> {
378 fn for_each_range<F: FnMut(gimli::Range)>(
379 &self,
380 unit: gimli::UnitRef<R>,
381 mut f: F,
382 ) -> Result<bool, Error> {
383 let mut added_any = false;
384 let mut add_range = |range: gimli::Range| {
385 if range.begin < range.end {
386 f(range);
387 added_any = true
388 }
389 };
390 if let Some(ranges_offset) = self.ranges_offset {
391 let mut range_list = unit.ranges(ranges_offset)?;
392 while let Some(range) = range_list.next()? {
393 add_range(range);
394 }
395 } else if let (Some(begin), Some(end)) = (self.low_pc, self.high_pc) {
396 add_range(gimli::Range { begin, end });
397 } else if let (Some(begin), Some(size)) = (self.low_pc, self.size) {
398 let end = begin.wrapping_add(size);
401 add_range(gimli::Range { begin, end });
402 }
403 Ok(added_any)
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 #[test]
410 fn context_is_send() {
411 fn assert_is_send<T: Send>() {}
412 assert_is_send::<crate::Context<gimli::read::EndianSlice<'_, gimli::LittleEndian>>>();
413 }
414}