use alloc::borrow::Cow; use alloc::string::String; use core::iter; use crate::{maybe_small, Error, Function, InlinedFunction, ResUnit}; /// A source location. pub struct Location<'a> { /// The file name. pub file: Option<&'a str>, /// The line number. pub line: Option, /// The column number. /// /// A value of `Some(0)` indicates the left edge. pub column: Option, } /// A function frame. pub struct Frame<'ctx, R: gimli::Reader> { /// The DWARF unit offset corresponding to the DIE of the function. pub dw_die_offset: Option>, /// The name of the function. pub function: Option>, /// The source location corresponding to this frame. pub location: Option>, } /// An iterator over function frames. pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>) where R: gimli::Reader; enum FrameIterState<'ctx, R> where R: gimli::Reader, { Empty, Location(Option>), Frames(FrameIterFrames<'ctx, R>), } struct FrameIterFrames<'ctx, R> where R: gimli::Reader, { unit: &'ctx ResUnit, sections: &'ctx gimli::Dwarf, function: &'ctx Function, inlined_functions: iter::Rev>>, next: Option>, } impl<'ctx, R> FrameIter<'ctx, R> where R: gimli::Reader + 'ctx, { pub(crate) fn new_empty() -> Self { FrameIter(FrameIterState::Empty) } pub(crate) fn new_location(location: Location<'ctx>) -> Self { FrameIter(FrameIterState::Location(Some(location))) } pub(crate) fn new_frames( unit: &'ctx ResUnit, sections: &'ctx gimli::Dwarf, function: &'ctx Function, inlined_functions: maybe_small::Vec<&'ctx InlinedFunction>, location: Option>, ) -> Self { FrameIter(FrameIterState::Frames(FrameIterFrames { unit, sections, function, inlined_functions: inlined_functions.into_iter().rev(), next: location, })) } /// Advances the iterator and returns the next frame. #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result>, Error> { let frames = match &mut self.0 { FrameIterState::Empty => return Ok(None), FrameIterState::Location(location) => { // We can't move out of a mutable reference, so use `take` instead. let location = location.take(); self.0 = FrameIterState::Empty; return Ok(Some(Frame { dw_die_offset: None, function: None, location, })); } FrameIterState::Frames(frames) => frames, }; let loc = frames.next.take(); let func = match frames.inlined_functions.next() { Some(func) => func, None => { let frame = Frame { dw_die_offset: Some(frames.function.dw_die_offset), function: frames.function.name.clone().map(|name| FunctionName { name, language: frames.unit.lang, }), location: loc, }; self.0 = FrameIterState::Empty; return Ok(Some(frame)); } }; let mut next = Location { file: None, line: if func.call_line != 0 { Some(func.call_line) } else { None }, column: if func.call_column != 0 { Some(func.call_column) } else { None }, }; if let Some(call_file) = func.call_file { if let Some(lines) = frames.unit.parse_lines(frames.sections)? { next.file = lines.file(call_file); } } frames.next = Some(next); Ok(Some(Frame { dw_die_offset: Some(func.dw_die_offset), function: func.name.clone().map(|name| FunctionName { name, language: frames.unit.lang, }), location: loc, })) } } #[cfg(feature = "fallible-iterator")] impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R> where R: gimli::Reader + 'ctx, { type Item = Frame<'ctx, R>; type Error = Error; #[inline] fn next(&mut self) -> Result>, Error> { self.next() } } /// A function name. pub struct FunctionName { /// The name of the function. pub name: R, /// The language of the compilation unit containing this function. pub language: Option, } impl FunctionName { /// The raw name of this function before demangling. pub fn raw_name(&self) -> Result, Error> { self.name.to_string_lossy() } /// The name of this function after demangling (if applicable). pub fn demangle(&self) -> Result, Error> { self.raw_name().map(|x| demangle_auto(x, self.language)) } } /// Demangle a symbol name using the demangling scheme for the given language. /// /// Returns `None` if demangling failed or is not required. #[allow(unused_variables)] pub fn demangle(name: &str, language: gimli::DwLang) -> Option { match language { #[cfg(feature = "rustc-demangle")] gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name) .ok() .as_ref() .map(|x| format!("{:#}", x)), #[cfg(feature = "cpp_demangle")] gimli::DW_LANG_C_plus_plus | gimli::DW_LANG_C_plus_plus_03 | gimli::DW_LANG_C_plus_plus_11 | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name) .ok() .and_then(|x| x.demangle(&Default::default()).ok()), _ => None, } } /// Apply 'best effort' demangling of a symbol name. /// /// If `language` is given, then only the demangling scheme for that language /// is used. /// /// If `language` is `None`, then heuristics are used to determine how to /// demangle the name. Currently, these heuristics are very basic. /// /// If demangling fails or is not required, then `name` is returned unchanged. pub fn demangle_auto(name: Cow<'_, str>, language: Option) -> Cow<'_, str> { match language { Some(language) => demangle(name.as_ref(), language), None => demangle(name.as_ref(), gimli::DW_LANG_Rust) .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)), } .map(Cow::from) .unwrap_or(name) }