extern crate backtrace; extern crate cpp_smoke_test; use std::sync::atomic::{AtomicBool, Ordering}; extern "C" { fn cpp_trampoline(func: extern "C" fn()) -> (); } #[test] #[ignore] // fixme(fitzgen/cpp_demangle#73) fn smoke_test_cpp() { static RAN_ASSERTS: AtomicBool = AtomicBool::new(false); extern "C" fn assert_cpp_frames() { let mut physical_frames = Vec::new(); backtrace::trace(|cx| { physical_frames.push(cx.ip()); // We only want to capture this closure's frame, assert_cpp_frames, // space::templated_trampoline, and cpp_trampoline. Those are // logical frames, which might be inlined into fewer physical // frames, so we may end up with extra logical frames after // resolving these. physical_frames.len() < 4 }); let names: Vec<_> = physical_frames .into_iter() .flat_map(|ip| { let mut logical_frame_names = vec![]; backtrace::resolve(ip, |sym| { let sym_name = sym.name().expect("Should have a symbol name"); let demangled = sym_name.to_string(); logical_frame_names.push(demangled); }); assert!( !logical_frame_names.is_empty(), "Should have resolved at least one symbol for the physical frame" ); logical_frame_names }) // Skip the backtrace::trace closure and assert_cpp_frames, and then // take the two C++ frame names. .skip_while(|name| !name.contains("trampoline")) .take(2) .collect(); println!("actual names = {:#?}", names); let expected = [ "void space::templated_trampoline(void (*)())", "cpp_trampoline", ]; println!("expected names = {:#?}", expected); assert_eq!(names.len(), expected.len()); for (actual, expected) in names.iter().zip(expected.iter()) { assert_eq!(actual, expected); } RAN_ASSERTS.store(true, Ordering::SeqCst); } assert!(!RAN_ASSERTS.load(Ordering::SeqCst)); unsafe { cpp_trampoline(assert_cpp_frames); } assert!(RAN_ASSERTS.load(Ordering::SeqCst)); }