Skip to content

Commit 7bb88c2

Browse files
jieyouxuPyr0de
andcommitted
tests: port backtrace's cpp_smoke_test as a run-make test
This test exercises the `backtrace` submodule included under the `library/` tree, which should be the `backtrace` that is used and thus transitively shipped with the standard library. The `cpp_smoke_test` was disabled in the `backtrace` repo many years ago. This is seemingly because the test failed years ago due to C++ symbol demangling troubles in `cpp_demangle` (which are now fixed; see <gimli-rs/cpp_demangle#73>). Note that this test is disabled on Windows MSVC because I couldn't get it to work locally: - At MSVC `-O1`, the test fails because there are no backtrace symbols. - At MSVC `-O0`, the test fails because both the C++ templated trampolined symbol has a different signature (w/ a `__cdecl`) and also `cpp_trampoline` is missing. Co-authored-by: Pyrode <[email protected]>
1 parent fb663e6 commit 7bb88c2

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
extern crate backtrace;
2+
3+
use std::sync::atomic::{AtomicBool, Ordering};
4+
5+
unsafe extern "C" {
6+
unsafe fn cpp_trampoline(func: extern "C" fn());
7+
}
8+
9+
fn main() {
10+
static RAN_ASSERTS: AtomicBool = AtomicBool::new(false);
11+
12+
extern "C" fn assert_cpp_frames() {
13+
let mut physical_frames = Vec::new();
14+
backtrace::trace(|cx| {
15+
physical_frames.push(cx.ip());
16+
17+
// We only want to capture:
18+
//
19+
// 1. this closure's frame
20+
// 2. `assert_cpp_frames`,
21+
// 3. `space::templated_trampoline`, and
22+
// 4. `cpp_trampoline`.
23+
//
24+
// Those are logical frames, which might be inlined into fewer physical frames, so we
25+
// may end up with extra logical frames after resolving these.
26+
physical_frames.len() < 4
27+
});
28+
29+
let names: Vec<_> = physical_frames
30+
.into_iter()
31+
.flat_map(|ip| {
32+
let mut logical_frame_names = vec![];
33+
34+
backtrace::resolve(ip, |sym| {
35+
let sym_name = sym.name().expect("Should have a symbol name");
36+
let demangled = sym_name.to_string();
37+
logical_frame_names.push(demangled);
38+
});
39+
40+
assert!(
41+
!logical_frame_names.is_empty(),
42+
"Should have resolved at least one symbol for the physical frame"
43+
);
44+
45+
logical_frame_names
46+
})
47+
// Skip the backtrace::trace closure and assert_cpp_frames, and then
48+
// take the two C++ frame names.
49+
.skip_while(|name| !name.contains("trampoline"))
50+
.take(2)
51+
.collect();
52+
53+
println!("actual names = {names:#?}");
54+
55+
let expected =
56+
["void space::templated_trampoline<void (*)()>(void (*)())", "cpp_trampoline"];
57+
println!("expected names = {expected:#?}");
58+
59+
assert_eq!(names.len(), expected.len());
60+
for (actual, expected) in names.iter().zip(expected.iter()) {
61+
assert_eq!(actual, expected);
62+
}
63+
64+
RAN_ASSERTS.store(true, Ordering::SeqCst);
65+
}
66+
67+
assert!(!RAN_ASSERTS.load(Ordering::SeqCst));
68+
unsafe {
69+
cpp_trampoline(assert_cpp_frames);
70+
}
71+
assert!(RAN_ASSERTS.load(Ordering::SeqCst));
72+
}
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! `backtrace`'s `cpp_smoke_test` ported to rust-lang/rust.
2+
//!
3+
//! A basic smoke test that exercises `backtrace` to see if it can resolve basic C++ templated +
4+
//! trampolined symbol names.
5+
6+
//@ ignore-cross-compile (binary needs to run)
7+
//@ only-nightly
8+
9+
//@ ignore-windows-msvc (test fails due to backtrace symbol mismatches)
10+
// FIXME: on MSVC, at `-O1`, there are no symbols available. At `-O0`, the test fails looking like:
11+
// ```
12+
// actual names = [
13+
// "space::templated_trampoline<void (__cdecl*)(void)>",
14+
// ]
15+
// expected names = [
16+
// "void space::templated_trampoline<void (*)()>(void (*)())",
17+
// "cpp_trampoline",
18+
// ]
19+
// ```
20+
21+
use run_make_support::rustc::sysroot;
22+
use run_make_support::{
23+
build_native_static_lib_cxx_optimized, cargo, crate_cc, cwd, path, rfs, run, rustc,
24+
source_root, target,
25+
};
26+
27+
fn main() {
28+
let target_dir = path("target");
29+
let src_root = source_root();
30+
let backtrace_submodule = src_root.join("library").join("backtrace");
31+
let backtrace_toml = backtrace_submodule.join("Cargo.toml");
32+
33+
// Build the `backtrace` package (the `library/backtrace` submodule to make sure we exercise the
34+
// same `backtrace` as shipped with std).
35+
cargo()
36+
// NOTE: needed to skip trying to link in `windows.0.52.0.lib` which is pre-built but not
37+
// available in *this* scenario.
38+
.env("RUSTFLAGS", "--cfg=windows_raw_dylib")
39+
.arg("build")
40+
.args(&["--manifest-path", &backtrace_toml.to_str().unwrap()])
41+
.args(&["--target", &target()])
42+
.arg("--features=cpp_demangle")
43+
.env("CARGO_TARGET_DIR", &target_dir)
44+
// Visual Studio 2022 requires that the LIB env var be set so it can
45+
// find the Windows SDK.
46+
.env("LIB", std::env::var("LIB").unwrap_or_default())
47+
.run();
48+
49+
let rlibs_path = target_dir.join(target()).join("debug").join("deps");
50+
51+
// FIXME: this test is *really* fragile. Even on `x86_64-unknown-linux-gnu`, this fails if a
52+
// different opt-level is passed. On `-O2` this test fails due to no symbols found. On `-O0`
53+
// this test fails because it's missing one of the expected symbols.
54+
build_native_static_lib_cxx_optimized("trampoline", "-O1");
55+
56+
rustc()
57+
.input("cpp_smoke_test.rs")
58+
.library_search_path(&rlibs_path)
59+
.library_search_path(cwd())
60+
.debuginfo("2")
61+
.arg("-ltrampoline")
62+
.run();
63+
64+
run("cpp_smoke_test");
65+
}
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+
}

0 commit comments

Comments
 (0)