Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref: Introduce unmangled Name and better DemangleOptions #275

Merged
merged 7 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
- `Unreal4ContextRuntimeProperties::misc_primary_cpu_brand` is has been removed.
- Deprecated Python APIs have been removed:
- `CodeModule.id` and `CodeModule.name` Use `debug_id` and `code_file`, respectively.
- `DemangleFormat` and public fields of `DemangleOptions` have been removed in favor of builder methods on `DemangleOptions`.
- `Name::new` now takes both the `NameMangling` state, and the `Language` explicitly.

## 7.5.0

Expand Down
14 changes: 10 additions & 4 deletions examples/addr2line/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ use std::borrow::Borrow;
use anyhow::{Context, Result};
use clap::{clap_app, ArgMatches};

use symbolic::common::{ByteView, Name};
use symbolic::common::{ByteView, Language, Name, NameMangling};
use symbolic::debuginfo::{Function, Object};
use symbolic::demangle::Demangle;
use symbolic::demangle::{Demangle, DemangleOptions};

fn print_name<'a, N: Borrow<Name<'a>>>(name: Option<N>, matches: &ArgMatches<'_>) {
match name.as_ref().map(Borrow::borrow) {
None => print!("??"),
Some(name) if name.as_str().is_empty() => print!("??"),
Some(name) if matches.is_present("demangle") => {
print!("{}", name.try_demangle(Default::default()));
print!("{}", name.try_demangle(DemangleOptions::name_only()));
}
Some(name) => print!("{}", name),
}
Expand Down Expand Up @@ -92,7 +92,13 @@ fn execute(matches: &ArgMatches<'_>) -> Result<()> {

if matches.is_present("functions") {
if let Some(symbol) = symbol_map.lookup(addr) {
print_name(symbol.name.as_ref().map(|n| Name::new(n.as_ref())), matches);
print_name(
symbol
.name
.as_ref()
.map(|n| Name::new(n.as_ref(), NameMangling::Mangled, Language::Unknown)),
matches,
);
print_range(symbol.address, Some(symbol.size), matches);
print!("\n at ");
}
Expand Down
5 changes: 3 additions & 2 deletions examples/minidump_stackwalk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use walkdir::WalkDir;

use symbolic::common::{Arch, ByteView, InstructionInfo, SelfCell};
use symbolic::debuginfo::{Archive, FileFormat, Object};
use symbolic::demangle::Demangle;
use symbolic::demangle::{Demangle, DemangleOptions};
use symbolic::minidump::cfi::CfiCache;
use symbolic::minidump::processor::{CodeModuleId, FrameInfoMap, ProcessState, StackFrame};
use symbolic::symcache::{LineInfo, SymCache, SymCacheError, SymCacheWriter};
Expand Down Expand Up @@ -204,7 +204,8 @@ fn print_state(
"{:>3} {}!{} [{} : {} + 0x{:x}]",
index,
module.debug_file(),
info.function_name().try_demangle(Default::default()),
info.function_name()
.try_demangle(DemangleOptions::name_only()),
info.filename(),
info.line(),
info.instruction_address() - info.line_address(),
Expand Down
8 changes: 6 additions & 2 deletions examples/symcache_debug/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use anyhow::{anyhow, Result};
use clap::{App, Arg, ArgMatches};

use symbolic::common::{Arch, ByteView, DSymPathExt, Language};
use symbolic::debuginfo::Archive;
use symbolic::demangle::Demangle;
use symbolic::symcache::{SymCache, SymCacheWriter};
use symbolic::{debuginfo::Archive, demangle::DemangleOptions};

fn execute(matches: &ArgMatches) -> Result<()> {
let buffer;
Expand Down Expand Up @@ -95,7 +95,11 @@ fn execute(matches: &ArgMatches) -> Result<()> {
println!("No match :(");
} else {
for sym in m {
print!("{}", sym.function_name().try_demangle(Default::default()));
print!(
"{}",
sym.function_name()
.try_demangle(DemangleOptions::name_only())
);

let path = sym.path();
let line = sym.line();
Expand Down
6 changes: 3 additions & 3 deletions py/tests/test_demangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

def test_swift_demangle():
mangled = "_TFC12Swift_Tester14ViewController11doSomethingfS0_FT_T_"
expected = "ViewController.doSomething(_:)"
expected = "ViewController.doSomething(ViewController)"
assert demangle_name(mangled, lang="Swift") == expected


def test_swift_demangle_implicit():
mangled = "_TFC12Swift_Tester14ViewController11doSomethingfS0_FT_T_"
expected = "ViewController.doSomething(_:)"
expected = "ViewController.doSomething(ViewController)"
assert demangle_name(mangled) == expected


Expand All @@ -21,7 +21,7 @@ def test_swift_demangle_options():
)
simplified_expected = (
u"protocol witness for static _ObjectiveCBridgeable._"
u"unconditionallyBridgeFromObjectiveC(_:) "
u"unconditionallyBridgeFromObjectiveC(A._ObjectiveCType?) "
u"in conformance UIApplicationLaunchOptionsKey"
)
assert demangle_name(mangled) == simplified_expected
Expand Down
18 changes: 6 additions & 12 deletions symbolic-cabi/src/demangle.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use symbolic::common::Name;
use symbolic::demangle::{Demangle, DemangleFormat, DemangleOptions};
use symbolic::common::{Name, NameMangling};
use symbolic::demangle::{Demangle, DemangleOptions};

use crate::core::SymbolicStr;

/// Creates a name from a string passed via FFI.
unsafe fn get_name(ident: *const SymbolicStr, lang: *const SymbolicStr) -> Name<'static> {
if lang.is_null() {
Name::new((*ident).as_str())
Name::from((*ident).as_str())
} else {
let lang = (*lang).as_str().parse().unwrap_or_default();
Name::with_language((*ident).as_str(), lang)
Name::new((*ident).as_str(), NameMangling::Unknown, lang)
}
}

Expand All @@ -23,10 +23,7 @@ ffi_fn! {
lang: *const SymbolicStr,
) -> Result<SymbolicStr> {
let name = get_name(ident, lang);
let demangled = name.try_demangle(DemangleOptions {
with_arguments: true,
format: DemangleFormat::Short,
});
let demangled = name.try_demangle(DemangleOptions::name_only().argument_types(true));

Ok(demangled.into_owned().into())
}
Expand All @@ -43,10 +40,7 @@ ffi_fn! {
lang: *const SymbolicStr,
) -> Result<SymbolicStr> {
let name = get_name(ident, lang);
let demangled = name.try_demangle(DemangleOptions {
with_arguments: false,
format: DemangleFormat::Short,
});
let demangled = name.try_demangle(DemangleOptions::name_only());

Ok(demangled.into_owned().into())
}
Expand Down
120 changes: 77 additions & 43 deletions symbolic-common/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,31 @@ impl str::FromStr for Language {
}
}

/// A [`Name`]s mangling state.
///
/// By default, the mangling of a [`Name`] is not known, but an explicit mangling state can be set
/// for Names that are guaranteed to be unmangled.
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_")
)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum NameMangling {
/// The [`Name`] is definitely mangled.
Mangled,
/// The [`Name`] is not mangled.
Unmangled,
/// The mangling of the [`Name`] is not known.
Unknown,
}

impl Default for NameMangling {
fn default() -> Self {
NameMangling::Unknown
}
}

/// The name of a potentially mangled symbol.
///
/// Debugging information often only contains mangled names in their symbol and debug information
Expand All @@ -656,16 +681,17 @@ impl str::FromStr for Language {
/// ```
/// use symbolic_common::Name;
///
/// let name = Name::new("_ZN3foo3barEv");
/// let name = Name::from("_ZN3foo3barEv");
/// assert_eq!(name.to_string(), "_ZN3foo3barEv");
/// ```
///
/// Create a name with a language. Alternate formatting prints the language:
/// Create a name with a language and explicit mangling state.
/// Alternate formatting prints the language:
///
/// ```
/// use symbolic_common::{Language, Name};
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let name = Name::with_language("_ZN3foo3barEv", Language::Cpp);
/// let name = Name::new("_ZN3foo3barEv", NameMangling::Mangled, Language::Cpp);
/// assert_eq!(format!("{:#}", name), "_ZN3foo3barEv [C++]");
/// ```
///
Expand All @@ -679,52 +705,34 @@ impl str::FromStr for Language {
pub struct Name<'a> {
string: Cow<'a, str>,
lang: Language,
#[cfg_attr(feature = "serde", serde(default))]
mangling: NameMangling,
}

impl<'a> Name<'a> {
/// Constructs a new mangled name.
///
/// The language of this name is `Language::Unknown`.
/// Constructs a new Name with given mangling and language.
///
/// # Example
/// In case both the mangling state and the language are unknown, a simpler alternative to use
/// is [`Name::from`].
///
/// ```
/// use symbolic_common::Name;
///
/// let name = Name::new("_ZN3foo3barEv");
/// assert_eq!(name.to_string(), "_ZN3foo3barEv");
/// ```
#[inline]
pub fn new<S>(string: S) -> Self
where
S: Into<Cow<'a, str>>,
{
Name {
string: string.into(),
lang: Language::Unknown,
}
}

/// Constructs a new mangled name with a known [`Language`].
///
/// # Example
///
/// ```
/// use symbolic_common::{Language, Name};
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let name = Name::with_language("_ZN3foo3barEv", Language::Cpp);
/// let name = Name::new("_ZN3foo3barEv", NameMangling::Mangled, Language::Cpp);
/// assert_eq!(format!("{:#}", name), "_ZN3foo3barEv [C++]");
/// ```
///
/// [`Language`]: enum.Language.html
#[inline]
pub fn with_language<S>(string: S, lang: Language) -> Self
pub fn new<S>(string: S, mangling: NameMangling, lang: Language) -> Self
where
S: Into<Cow<'a, str>>,
{
Name {
string: string.into(),
lang,
mangling,
}
}

Expand All @@ -733,24 +741,30 @@ impl<'a> Name<'a> {
/// # Example
///
/// ```
/// use symbolic_common::Name;
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let name = Name::new("_ZN3foo3barEv");
/// let name = Name::new("_ZN3foo3barEv", NameMangling::Mangled, Language::Cpp);
/// assert_eq!(name.as_str(), "_ZN3foo3barEv");
/// ```
///
/// This is also available as an `AsRef<str>` implementation:
///
/// ```
/// use symbolic_common::Name;
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let name = Name::new("_ZN3foo3barEv");
/// let name = Name::new("_ZN3foo3barEv", NameMangling::Mangled, Language::Cpp);
/// assert_eq!(name.as_ref(), "_ZN3foo3barEv");
/// ```
pub fn as_str(&self) -> &str {
&self.string
}

/// Set the `Name`'s language.
pub fn set_language(&mut self, language: Language) -> &mut Self {
self.lang = language;
self
}

/// The language of the mangled symbol.
///
/// If the language is not declared in the source, this returns `Language::Unknown`. The
Expand All @@ -760,37 +774,57 @@ impl<'a> Name<'a> {
/// # Example
///
/// ```
/// use symbolic_common::{Language, Name};
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let name = Name::new("_ZN3foo3barEv");
/// assert_eq!(name.language(), Language::Unknown);
/// let name = Name::new("_ZN3foo3barEv", NameMangling::Mangled, Language::Cpp);
/// assert_eq!(name.language(), Language::Cpp);
/// ```
pub fn language(&self) -> Language {
self.lang
}

/// Converts this name into a `Cow`, dropping the language.
/// Set the `Name`'s mangling state.
pub fn set_mangling(&mut self, mangling: NameMangling) -> &mut Self {
self.mangling = mangling;
self
}

/// Returns the `Name`'s mangling state.
///
/// # Example
///
/// ```
/// use symbolic_common::{Language, Name, NameMangling};
///
/// let unmangled = Name::new("foo::bar", NameMangling::Unmangled, Language::Unknown);
/// assert_eq!(unmangled.mangling(), NameMangling::Unmangled);
/// ```
pub fn mangling(&self) -> NameMangling {
self.mangling
}

/// Converts this name into a [`Cow`].
///
/// # Example
///
/// ```
/// use symbolic_common::Name;
///
/// let name = Name::new("_ZN3foo3barEv");
/// let name = Name::from("_ZN3foo3barEv");
Swatinem marked this conversation as resolved.
Show resolved Hide resolved
/// assert_eq!(name.into_cow(), "_ZN3foo3barEv");
/// ```
pub fn into_cow(self) -> Cow<'a, str> {
self.string
}

/// Converts this name into a `String`, dropping the language.
/// Converts this name into a [`String`].
///
/// # Example
///
/// ```
/// use symbolic_common::Name;
///
/// let name = Name::new("_ZN3foo3barEv");
/// let name = Name::from("_ZN3foo3barEv");
/// assert_eq!(name.into_string(), "_ZN3foo3barEv");
/// ```
pub fn into_string(self) -> String {
Expand All @@ -815,7 +849,7 @@ where
S: Into<Cow<'a, str>>,
{
fn from(string: S) -> Self {
Self::new(string)
Self::new(string, NameMangling::Unknown, Language::Unknown)
}
}

Expand Down
4 changes: 2 additions & 2 deletions symbolic-debuginfo/src/breakpad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::str;
use pest::Parser;
use thiserror::Error;

use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Name};
use symbolic_common::{Arch, AsSelf, CodeId, DebugId, Language, Name, NameMangling};

use crate::base::*;
use crate::private::{Lines, Parse};
Expand Down Expand Up @@ -1138,7 +1138,7 @@ impl<'s> BreakpadFunctionIterator<'s> {
Ok(Function {
address: record.address,
size: record.size,
name: Name::from(record.name),
name: Name::new(record.name, NameMangling::Unmangled, Language::Unknown),
compilation_dir: &[],
lines,
inlinees: Vec::new(),
Expand Down
Loading