From 7764e8ace3b22dff544fe2722d238d060f13e55b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <jieyouxu@outlook.com>
Date: Sat, 23 Mar 2024 18:10:14 +0000
Subject: [PATCH] Port backtrace dylib-dep test to a ui test

---
 .../auxiliary/dylib-dep-helper-aux.rs         |  17 +++
 .../debuginfo/auxiliary/dylib-dep-helper.rs   |  19 +++
 tests/ui/debuginfo/backtrace-dylib-dep.rs     | 114 ++++++++++++++++++
 3 files changed, 150 insertions(+)
 create mode 100644 tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs
 create mode 100644 tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs
 create mode 100644 tests/ui/debuginfo/backtrace-dylib-dep.rs

diff --git a/tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs b/tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs
new file mode 100644
index 00000000000..d45e0bcd3b4
--- /dev/null
+++ b/tests/ui/debuginfo/auxiliary/dylib-dep-helper-aux.rs
@@ -0,0 +1,17 @@
+//@ compile-flags: -g -Cstrip=none -Cforce-frame-pointers=yes
+
+#[inline(never)]
+pub fn callback<F>(f: F)
+where
+    F: FnOnce((&'static str, u32)),
+{
+    f((file!(), line!()))
+}
+
+#[inline(always)]
+pub fn callback_inlined<F>(f: F)
+where
+    F: FnOnce((&'static str, u32)),
+{
+    f((file!(), line!()))
+}
diff --git a/tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs b/tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs
new file mode 100644
index 00000000000..565d8b65de0
--- /dev/null
+++ b/tests/ui/debuginfo/auxiliary/dylib-dep-helper.rs
@@ -0,0 +1,19 @@
+//@ compile-flags: -g -Cstrip=none -Cforce-frame-pointers=yes
+
+#![crate_type = "cdylib"]
+#![crate_type = "rlib"]
+
+#![allow(improper_ctypes_definitions)]
+
+type Pos = (&'static str, u32);
+
+macro_rules! pos {
+    () => {
+        (file!(), line!())
+    };
+}
+
+#[no_mangle]
+pub extern "C" fn foo(outer: Pos, inner: fn(Pos, Pos)) {
+    inner(outer, pos!());
+}
diff --git a/tests/ui/debuginfo/backtrace-dylib-dep.rs b/tests/ui/debuginfo/backtrace-dylib-dep.rs
new file mode 100644
index 00000000000..e2414073ede
--- /dev/null
+++ b/tests/ui/debuginfo/backtrace-dylib-dep.rs
@@ -0,0 +1,114 @@
+// 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: <https://github.com/rust-lang/rust/issues/122899>.
+// Original test:
+// <https://github.com/rust-lang/backtrace-rs/tree/6fa4b85b9962c3e1be8c2e5cc605cd078134152b/crates/dylib-dep>
+// 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
+//@ 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!())
+    };
+}
+
+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!());
+}