Skip to content

Commit 9aea357

Browse files
committed
Add support for demangling C++ frames' symbols
This adds a new optional (but on-by-default) dependency on the `cpp_demangle` crate. When this feature is enabled, if demangling a frame's symbol as a Rust symbol fails, then we attempt to demangle it as a C++ symbol.
1 parent 4219027 commit 9aea357

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ rustc-demangle = "0.1"
2222
serde = { version = "0.8", optional = true }
2323
rustc-serialize = { version = "0.3", optional = true }
2424

25+
# Optionally demangle C++ frames' symbols in backtraces.
26+
cpp_demangle = { version = "0.2", optional = true }
27+
2528
[target.'cfg(windows)'.dependencies]
2629
dbghelp-sys = { version = "0.2", optional = true }
2730
kernel32-sys = { version = "0.2", optional = true }
@@ -42,7 +45,7 @@ serde_codegen = { version = "0.8", optional = true }
4245
# Note that not all features are available on all platforms, so even though a
4346
# feature is enabled some other feature may be used instead.
4447
[features]
45-
default = ["libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp"]
48+
default = ["libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp", "cpp_demangle"]
4649

4750
#=======================================
4851
# Methods of acquiring a backtrace

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ extern crate cfg_if;
8585

8686
extern crate rustc_demangle;
8787

88+
#[cfg(feature = "cpp_demangle")]
89+
extern crate cpp_demangle;
90+
8891
#[allow(dead_code)] // not used everywhere
8992
#[cfg(unix)]
9093
#[macro_use]

src/symbolize/mod.rs

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
use std::borrow::Cow;
12
use std::fmt;
3+
#[cfg(not(feature = "cpp_demangle"))]
4+
use std::marker::PhantomData;
25
use std::os::raw::c_void;
36
use std::path::Path;
47
use std::str;
@@ -108,26 +111,82 @@ impl fmt::Debug for Symbol {
108111
}
109112
}
110113

114+
// Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust failed.
115+
#[cfg(feature = "cpp_demangle")]
116+
struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
117+
118+
#[cfg(feature = "cpp_demangle")]
119+
impl<'a> OptionCppSymbol<'a> {
120+
fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
121+
OptionCppSymbol(::cpp_demangle::BorrowedSymbol::with_tail(input)
122+
.ok()
123+
.map(|(sym, _tail)| sym))
124+
}
125+
126+
fn none() -> OptionCppSymbol<'a> {
127+
OptionCppSymbol(None)
128+
}
129+
130+
fn as_str(&self) -> Option<Cow<'a, str>> {
131+
self.0.as_ref().map(|cpp| Cow::from(format!("{}", cpp)))
132+
}
133+
}
134+
135+
// Make sure to keep this zero-sized, so that the `cpp_demangle` feature has no
136+
// cost when disabled.
137+
#[cfg(not(feature = "cpp_demangle"))]
138+
struct OptionCppSymbol<'a>(PhantomData<&'a ()>);
139+
140+
#[cfg(not(feature = "cpp_demangle"))]
141+
impl<'a> OptionCppSymbol<'a> {
142+
fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> {
143+
OptionCppSymbol(PhantomData)
144+
}
145+
146+
fn none() -> OptionCppSymbol<'a> {
147+
OptionCppSymbol(PhantomData)
148+
}
149+
150+
fn as_str(&self) -> Option<Cow<'a, str>> {
151+
None
152+
}
153+
}
154+
111155
/// A wrapper around a symbol name to provide ergonomic accessors to the
112156
/// demangled name, the raw bytes, the raw string, etc.
113157
pub struct SymbolName<'a> {
114158
bytes: &'a [u8],
115159
demangled: Option<Demangle<'a>>,
160+
cpp_demangled: OptionCppSymbol<'a>,
116161
}
117162

118163
impl<'a> SymbolName<'a> {
119164
/// Creates a new symbol name from the raw underlying bytes.
120165
pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
121166
let demangled = str::from_utf8(bytes).ok().map(demangle);
167+
168+
// Only try and parse a C++ symbol if we didn't parse a Rust symbol.
169+
let cpp = if demangled.is_none() {
170+
OptionCppSymbol::parse(bytes)
171+
} else {
172+
OptionCppSymbol::none()
173+
};
174+
122175
SymbolName {
123176
bytes: bytes,
124177
demangled: demangled,
178+
cpp_demangled: cpp,
125179
}
126180
}
127181

128-
/// Returns the raw symbol name as `&str` if the symbols is valid utf-8.
129-
pub fn as_str(&self) -> Option<&'a str> {
130-
self.demangled.as_ref().map(|s| s.as_str())
182+
/// Returns the raw symbol name as a `str` if the symbols is valid utf-8.
183+
pub fn as_str(&self) -> Option<Cow<'a, str>> {
184+
self.demangled
185+
.as_ref()
186+
.map(|s| Cow::from(s.as_str()))
187+
.or_else(|| {
188+
self.cpp_demangled.as_str()
189+
})
131190
}
132191

133192
/// Returns the raw symbol name as a list of bytes
@@ -137,6 +196,18 @@ impl<'a> SymbolName<'a> {
137196
}
138197

139198
impl<'a> fmt::Display for SymbolName<'a> {
199+
#[cfg(feature = "cpp_demangle")]
200+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201+
if let Some(ref s) = self.demangled {
202+
s.fmt(f)
203+
} else if let Some(ref cpp) = self.cpp_demangled.0 {
204+
cpp.fmt(f)
205+
} else {
206+
String::from_utf8_lossy(self.bytes).fmt(f)
207+
}
208+
}
209+
210+
#[cfg(not(feature = "cpp_demangle"))]
140211
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141212
if let Some(ref s) = self.demangled {
142213
s.fmt(f)
@@ -147,6 +218,18 @@ impl<'a> fmt::Display for SymbolName<'a> {
147218
}
148219

149220
impl<'a> fmt::Debug for SymbolName<'a> {
221+
#[cfg(feature = "cpp_demangle")]
222+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223+
if let Some(ref s) = self.demangled {
224+
s.fmt(f)
225+
} else if let Some(ref cpp) = self.cpp_demangled.0 {
226+
cpp.fmt(f)
227+
} else {
228+
String::from_utf8_lossy(self.bytes).fmt(f)
229+
}
230+
}
231+
232+
#[cfg(not(feature = "cpp_demangle"))]
150233
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151234
if let Some(ref s) = self.demangled {
152235
s.fmt(f)
@@ -185,4 +268,3 @@ cfg_if! {
185268
use self::noop::Symbol as SymbolImp;
186269
}
187270
}
188-

0 commit comments

Comments
 (0)