-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #292 from philipc/issue-10
Break up lib.rs
- Loading branch information
Showing
6 changed files
with
1,410 additions
and
1,298 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.