2 // We disable tail merging here because it can't preserve debuginfo and thus
3 // potentially breaks the backtraces. Also, subtle changes can decide whether
4 // tail merging succeeds, so the test might work today but fail tomorrow due to a
5 // seemingly completely unrelated change.
6 // Unfortunately, LLVM has no "disable" option for this, so we have to set
7 // "enable" to 0 instead.
9 // compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0
10 // compile-flags:-Cforce-frame-pointers=yes
11 // compile-flags:-Cstrip=none
12 // ignore-pretty issue #37195
13 // ignore-emscripten spawning processes is not supported
14 // ignore-sgx no processes
18 #[path = "backtrace-debuginfo-aux.rs"] mod aux;
21 () => ((file!(), line!()))
24 macro_rules! dump_and_die {
25 ($($pos:expr),*) => ({
26 // FIXME(#18285): we cannot include the current position because
27 // the macro span takes over the last frame's file/line.
29 // You might also be wondering why a major platform,
30 // i686-pc-windows-msvc, is located in here. Some of the saga can be
31 // found on #62897, but the tl;dr; is that it appears that if the
32 // standard library doesn't have debug information or frame pointers,
33 // which it doesn't by default on the test builders, then the stack
34 // walking routines in dbghelp will randomly terminate the stack trace
35 // in libstd without going further. Presumably the addition of frame
36 // pointers and/or debuginfo fixes this since tests always work with
37 // nightly compilers (which have debuginfo). In general though this test
38 // is replicated in rust-lang/backtrace-rs and has extensive coverage
39 // there, even on i686-pc-windows-msvc. We do the best we can in
40 // rust-lang/rust to test it as well, but sometimes we just gotta keep
42 if cfg!(any(target_os = "android",
43 all(target_os = "linux", target_arch = "arm"),
44 all(target_env = "msvc", target_arch = "x86"),
45 target_os = "freebsd",
46 target_os = "dragonfly",
47 target_os = "openbsd")) {
48 // skip these platforms as this support isn't implemented yet.
50 dump_filelines(&[$($pos),*]);
56 // we can't use a function as it will alter the backtrace
58 ($counter:expr; $($pos:expr),*) => ({
60 dump_and_die!($($pos),*)
67 type Pos = (&'static str, u32);
69 // this goes to stdout and each line has to be occurred
70 // in the following backtrace to stderr with a correct order.
71 fn dump_filelines(filelines: &[Pos]) {
72 for &(file, line) in filelines.iter().rev() {
74 let basename = file.split(&['/', '\\'][..]).last().unwrap();
75 println!("{}:{}", basename, line);
80 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
81 check!(counter; main_pos, outer_pos);
82 check!(counter; main_pos, outer_pos);
83 let inner_pos = pos!(); aux::callback(|aux_pos| {
84 check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
86 let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
87 check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
91 // We emit the wrong location for the caller here when inlined on MSVC
92 #[cfg_attr(not(target_env = "msvc"), inline(always))]
93 #[cfg_attr(target_env = "msvc", inline(never))]
94 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
95 check!(counter; main_pos, outer_pos);
96 check!(counter; main_pos, outer_pos);
98 // Again, disable inlining for MSVC.
99 #[cfg_attr(not(target_env = "msvc"), inline(always))]
100 #[cfg_attr(target_env = "msvc", inline(never))]
101 fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
102 check!(counter; main_pos, outer_pos, inner_pos);
104 inner_further_inlined(counter, main_pos, outer_pos, pos!());
106 let inner_pos = pos!(); aux::callback(|aux_pos| {
107 check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
109 let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
110 check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
113 // this tests a distinction between two independent calls to the inlined function.
114 // (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
115 inner_further_inlined(counter, main_pos, outer_pos, pos!());
119 fn outer(mut counter: i32, main_pos: Pos) {
120 inner(&mut counter, main_pos, pos!());
121 inner_inlined(&mut counter, main_pos, pos!());
124 fn check_trace(output: &str, error: &str) -> Result<(), String> {
125 // reverse the position list so we can start with the last item (which was the first line)
126 let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect();
128 if !error.contains("stack backtrace") {
129 return Err(format!("no backtrace found in stderr:\n{}", error))
131 for line in error.lines() {
132 if !remaining.is_empty() && line.contains(remaining.last().unwrap()) {
136 if !remaining.is_empty() {
137 return Err(format!("trace does not match position list\n\
138 still need to find {:?}\n\n\
141 remaining, output, error))
146 fn run_test(me: &str) {
148 use std::process::Command;
151 let mut errors = Vec::new();
153 let out = Command::new(me)
154 .env("RUST_BACKTRACE", "full")
155 .arg(i.to_string()).output().unwrap();
156 let output = str::from_utf8(&out.stdout).unwrap();
157 let error = str::from_utf8(&out.stderr).unwrap();
158 if out.status.success() {
159 assert!(output.contains("done."), "bad output for successful run: {}", output);
162 if let Err(e) = check_trace(output, error) {
168 if errors.len() > 0 {
169 for error in errors {
170 println!("---------------------------------------");
171 println!("{}", error);
174 panic!("found some errors");
180 let args: Vec<String> = env::args().collect();
182 let case = args[1].parse().unwrap();
183 eprintln!("test case {}", case);