Skip to content

Commit e6df549

Browse files
committed
Add a smoke test for capturing a backtrace with C++ symbols and demangling them
1 parent a135194 commit e6df549

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ backtrace-sys = { path = "backtrace-sys", version = "0.1.3", optional = true }
3535

3636
[build-dependencies]
3737
serde_codegen = { version = "0.8", optional = true }
38+
gcc = { version = "0.3.43", optional = true }
3839

3940
# Each feature controls the two phases of finding a backtrace: getting a
4041
# backtrace and then resolving instruction pointers to symbols. The default
@@ -45,7 +46,7 @@ serde_codegen = { version = "0.8", optional = true }
4546
# Note that not all features are available on all platforms, so even though a
4647
# feature is enabled some other feature may be used instead.
4748
[features]
48-
default = ["libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp", "cpp_demangle"]
49+
default = ["libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp", "demangle_cpp"]
4950

5051
#=======================================
5152
# Methods of acquiring a backtrace
@@ -79,9 +80,12 @@ default = ["libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp"
7980
# enough on OSX.
8081
# - coresymbolication: this feature uses the undocumented core symbolication
8182
# framework on OS X to symbolize.
83+
# - demangle_cpp: this feature enables demangling C++ symbols from C++
84+
# frames.
8285
libbacktrace = ["backtrace-sys"]
8386
dladdr = []
8487
coresymbolication = []
88+
demangle_cpp = ["cpp_demangle", "gcc"]
8589

8690
#=======================================
8791
# Methods of serialization

build.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ fn main() {
2525

2626
expand_serde(out_dir);
2727
println!("cargo:rerun-if-changed=src/capture.rs");
28+
29+
compile_cpp();
30+
println!("cargo:rerun-if-changed=cpp/trampoline.cpp");
2831
}
2932

3033
#[cfg(not(feature = "serialize-serde"))]
@@ -49,3 +52,23 @@ fn expand_serde(out_dir: &Path) {
4952
serde_codegen::expand(&out_dir.join("tmp.rs"), &dst).unwrap();
5053
}).unwrap().join().unwrap();
5154
}
55+
56+
// TODO: how to only do this for tests? I tried to do:
57+
//
58+
// > #[cfg(all(test, feature = "demangle_cpp"))]
59+
//
60+
// but it did not work...
61+
#[cfg(feature = "demangle_cpp")]
62+
fn compile_cpp() {
63+
extern crate gcc;
64+
65+
gcc::Config::new()
66+
.cpp(true)
67+
.debug(true)
68+
.opt_level(0)
69+
.file("cpp/trampoline.cpp")
70+
.compile("libcpptrampoline.a");
71+
}
72+
73+
#[cfg(not(feature = "demangle_cpp"))]
74+
fn compile_cpp() {}

cpp/trampoline.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <stdio.h>
2+
3+
namespace space {
4+
template <typename FuncT>
5+
void templated_trampoline(FuncT func) {
6+
func();
7+
}
8+
}
9+
10+
typedef void (*FuncPtr)();
11+
12+
extern "C" void cpp_trampoline(FuncPtr func) {
13+
space::templated_trampoline(func);
14+
}

tests/smoke.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,58 @@ fn is_serde() {
165165
is_serialize::<backtrace::Backtrace>();
166166
is_deserialize::<backtrace::Backtrace>();
167167
}
168+
169+
#[test]
170+
#[inline(never)]
171+
#[cfg(all(not(target_os = "windows"), feature = "demangle_cpp"))]
172+
fn smoke_test_cpp() {
173+
use std::sync::atomic::{ATOMIC_BOOL_INIT, AtomicBool, Ordering};
174+
175+
extern "C" {
176+
fn cpp_trampoline(func: extern "C" fn()) -> ();
177+
}
178+
179+
static RAN_ASSERTS: AtomicBool = ATOMIC_BOOL_INIT;
180+
181+
extern "C" fn assert_cpp_frames() {
182+
let mut frames = Vec::new();
183+
backtrace::trace(|cx| {
184+
frames.push(cx.ip());
185+
186+
// Only capture this closure's frame, assert_cpp_frames,
187+
// space::templated_trampoline, cpp_trampoline, and
188+
// smoke_test_cpp. They should all be physical frames, and none
189+
// should be inlined.
190+
frames.len() < 5
191+
});
192+
193+
let names: Vec<_> = frames.iter()
194+
.map(|ip| {
195+
let mut name = None;
196+
backtrace::resolve(*ip, |sym| {
197+
assert!(name.is_none(), "Shouldn't have any inlined frames");
198+
name = Some(sym.name().unwrap().to_string());
199+
});
200+
name.unwrap()
201+
})
202+
.collect();
203+
204+
println!("FITZGEN: names = {:?}", names);
205+
206+
assert_eq!(names, [
207+
"backtrace::backtrace::trace<closure>",
208+
"smoke::smoke_test_cpp::assert_cpp_frames",
209+
"void space::templated_trampoline<void (*)()>(void (*)())",
210+
"cpp_trampoline",
211+
"smoke::smoke_test_cpp"
212+
]);
213+
214+
RAN_ASSERTS.store(true, Ordering::SeqCst);
215+
}
216+
217+
assert!(!RAN_ASSERTS.load(Ordering::SeqCst));
218+
unsafe {
219+
cpp_trampoline(assert_cpp_frames);
220+
}
221+
assert!(RAN_ASSERTS.load(Ordering::SeqCst));
222+
}

0 commit comments

Comments
 (0)