Skip to content

Commit 283f0d3

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 283f0d3

File tree

3 files changed

+94
-5
lines changed

3 files changed

+94
-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: 87 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,83 @@ 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(input: &'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.
157+
#[cfg(feature = "cpp_demangle")]
113158
pub struct SymbolName<'a> {
114159
bytes: &'a [u8],
115160
demangled: Option<Demangle<'a>>,
161+
cpp_demangled: OptionCppSymbol<'a>,
116162
}
117163

118164
impl<'a> SymbolName<'a> {
119165
/// Creates a new symbol name from the raw underlying bytes.
120166
pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
121167
let demangled = str::from_utf8(bytes).ok().map(demangle);
168+
169+
// Only try and parse a C++ symbol if we didn't parse a Rust symbol.
170+
let cpp = if demangled.is_none() {
171+
OptionCppSymbol::parse(bytes)
172+
} else {
173+
OptionCppSymbol::none()
174+
};
175+
122176
SymbolName {
123177
bytes: bytes,
124178
demangled: demangled,
179+
cpp_demangled: cpp,
125180
}
126181
}
127182

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())
183+
/// Returns the raw symbol name as a `str` if the symbols is valid utf-8.
184+
pub fn as_str(&self) -> Option<Cow<'a, str>> {
185+
self.demangled
186+
.as_ref()
187+
.map(|s| Cow::from(s.as_str()))
188+
.or_else(|| {
189+
self.cpp_demangled.as_str()
190+
})
131191
}
132192

133193
/// Returns the raw symbol name as a list of bytes
@@ -137,6 +197,18 @@ impl<'a> SymbolName<'a> {
137197
}
138198

139199
impl<'a> fmt::Display for SymbolName<'a> {
200+
#[cfg(feature = "cpp_demangle")]
201+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202+
if let Some(ref s) = self.demangled {
203+
s.fmt(f)
204+
} else if let Some(ref cpp) = self.cpp_demangled.0 {
205+
cpp.fmt(f)
206+
} else {
207+
String::from_utf8_lossy(self.bytes).fmt(f)
208+
}
209+
}
210+
211+
#[cfg(not(feature = "cpp_demangle"))]
140212
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141213
if let Some(ref s) = self.demangled {
142214
s.fmt(f)
@@ -147,6 +219,18 @@ impl<'a> fmt::Display for SymbolName<'a> {
147219
}
148220

149221
impl<'a> fmt::Debug for SymbolName<'a> {
222+
#[cfg(feature = "cpp_demangle")]
223+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224+
if let Some(ref s) = self.demangled {
225+
s.fmt(f)
226+
} else if let Some(ref cpp) = self.cpp_demangled.0 {
227+
cpp.fmt(f)
228+
} else {
229+
String::from_utf8_lossy(self.bytes).fmt(f)
230+
}
231+
}
232+
233+
#[cfg(not(feature = "cpp_demangle"))]
150234
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151235
if let Some(ref s) = self.demangled {
152236
s.fmt(f)
@@ -185,4 +269,3 @@ cfg_if! {
185269
use self::noop::Symbol as SymbolImp;
186270
}
187271
}
188-

0 commit comments

Comments
 (0)