Skip to content

Commit

Permalink
Merge pull request #292 from philipc/issue-10
Browse files Browse the repository at this point in the history
Break up lib.rs
  • Loading branch information
philipc authored Apr 15, 2024
2 parents 274a90c + 9bfe46d commit 52ee0a1
Show file tree
Hide file tree
Showing 6 changed files with 1,410 additions and 1,298 deletions.
220 changes: 220 additions & 0 deletions src/frame.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
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<u32>,
/// The column number.
///
/// A value of `Some(0)` indicates the left edge.
pub column: Option<u32>,
}

/// 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<gimli::UnitOffset<R::Offset>>,
/// The name of the function.
pub function: Option<FunctionName<R>>,
/// The source location corresponding to this frame.
pub location: Option<Location<'ctx>>,
}

/// 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<Location<'ctx>>),
Frames(FrameIterFrames<'ctx, R>),
}

struct FrameIterFrames<'ctx, R>
where
R: gimli::Reader,
{
unit: &'ctx ResUnit<R>,
sections: &'ctx gimli::Dwarf<R>,
function: &'ctx Function<R>,
inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
next: Option<Location<'ctx>>,
}

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<R>,
sections: &'ctx gimli::Dwarf<R>,
function: &'ctx Function<R>,
inlined_functions: maybe_small::Vec<&'ctx InlinedFunction<R>>,
location: Option<Location<'ctx>>,
) -> 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.
pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, 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<Option<Frame<'ctx, R>>, Error> {
self.next()
}
}

/// A function name.
pub struct FunctionName<R: gimli::Reader> {
/// The name of the function.
pub name: R,
/// The language of the compilation unit containing this function.
pub language: Option<gimli::DwLang>,
}

impl<R: gimli::Reader> FunctionName<R> {
/// The raw name of this function before demangling.
pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> {
self.name.to_string_lossy()
}

/// The name of this function after demangling (if applicable).
pub fn demangle(&self) -> Result<Cow<'_, str>, 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<String> {
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<gimli::DwLang>) -> 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)
}
5 changes: 2 additions & 3 deletions src/function.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::iter;

use crate::lazy::LazyCell;
use crate::maybe_small;
Expand Down Expand Up @@ -316,7 +315,7 @@ impl<R: gimli::Reader> Function<R> {
pub(crate) fn find_inlined_functions(
&self,
probe: u64,
) -> iter::Rev<maybe_small::IntoIter<&InlinedFunction<R>>> {
) -> maybe_small::Vec<&InlinedFunction<R>> {
// `inlined_functions` is ordered from outside to inside.
let mut inlined_functions = maybe_small::Vec::new();
let mut inlined_addresses = &self.inlined_addresses[..];
Expand Down Expand Up @@ -347,7 +346,7 @@ impl<R: gimli::Reader> Function<R> {
break;
}
}
inlined_functions.into_iter().rev()
inlined_functions
}
}

Expand Down
Loading

0 comments on commit 52ee0a1

Please sign in to comment.