// Check that backtrace info is correctly generated for dynamic libraries and is usable by a // rust binary. // Part of porting some backtrace tests to rustc: . // Original test: // // ignore-tidy-linelength //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized //@ ignore-musl musl doesn't support dynamic libraries (at least when the original test was written). //@ needs-unwind //@ compile-flags: -g -Copt-level=0 -Cstrip=none -Cforce-frame-pointers=yes //@ ignore-emscripten Requires custom symbolization code //@ aux-crate: dylib_dep_helper=dylib-dep-helper.rs //@ aux-crate: auxiliary=dylib-dep-helper-aux.rs //@ run-pass #![allow(improper_ctypes)] #![allow(improper_ctypes_definitions)] extern crate dylib_dep_helper; extern crate auxiliary; use std::backtrace::Backtrace; macro_rules! pos { () => { (file!(), line!()) }; } #[collapse_debuginfo(yes)] macro_rules! check { ($($pos:expr),*) => ({ verify(&[$($pos,)* pos!()]); }) } fn verify(filelines: &[Pos]) { let trace = Backtrace::capture(); eprintln!("-----------------------------------"); eprintln!("looking for:"); for (file, line) in filelines.iter().rev() { eprintln!("\t{file}:{line}"); } eprintln!("found:\n{trace:#?}"); let mut iter = filelines.iter().rev(); // FIXME(jieyouxu): use proper `BacktraceFrame` accessors when it becomes available. Right now, // this depends on the debug format of `Backtrace` which is of course fragile. let backtrace = format!("{:#?}", trace); while let Some((file, line)) = iter.next() { // FIXME(jieyouxu): make this test use proper accessors on `BacktraceFrames` once it has // them. assert!(backtrace.contains(file), "expected backtrace to contain {}", file); assert!(backtrace.contains(&line.to_string()), "expected backtrace to contain {}", line); } } type Pos = (&'static str, u32); extern "C" { #[link_name = "foo"] fn foo(p: Pos, cb: fn(Pos, Pos)); } fn main() { std::env::set_var("RUST_BACKTRACE", "1"); unsafe { foo(pos!(), |a, b| { check!(a, b) }) } outer(pos!()); } #[inline(never)] fn outer(main_pos: Pos) { inner(main_pos, pos!()); inner_inlined(main_pos, pos!()); } #[inline(never)] fn inner(main_pos: Pos, outer_pos: Pos) { check!(main_pos, outer_pos); check!(main_pos, outer_pos); let inner_pos = pos!(); auxiliary::callback(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); } #[inline(always)] fn inner_inlined(main_pos: Pos, outer_pos: Pos) { check!(main_pos, outer_pos); check!(main_pos, outer_pos); #[inline(always)] fn inner_further_inlined(main_pos: Pos, outer_pos: Pos, inner_pos: Pos) { check!(main_pos, outer_pos, inner_pos); } inner_further_inlined(main_pos, outer_pos, pos!()); let inner_pos = pos!(); auxiliary::callback(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); // this tests a distinction between two independent calls to the inlined function. // (un)fortunately, LLVM somehow merges two consecutive such calls into one node. inner_further_inlined(main_pos, outer_pos, pos!()); }