]> git.lizzy.rs Git - rust.git/blob - src/test/ui/backtrace-debuginfo.rs
Rollup merge of #62360 - Aaron1011:patch-2, r=RalfJung
[rust.git] / src / test / ui / backtrace-debuginfo.rs
1 // run-pass
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.
8
9 // compile-flags:-g -Cllvm-args=-enable-tail-merge=0 -Cllvm-args=-opt-bisect-limit=0
10 // compile-flags:-Cforce-frame-pointers=yes
11 // ignore-pretty issue #37195
12 // ignore-cloudabi spawning processes is not supported
13 // ignore-emscripten spawning processes is not supported
14 // ignore-msvc issue #62844
15 // ignore-sgx no processes
16 // normalize-stderr-test ".*\n" -> ""
17
18 // Note that above `-opt-bisect-limit=0` is used to basically disable
19 // optimizations. It creates tons of output on stderr, hence we normalize
20 // that away entirely.
21
22 use std::env;
23
24 #[path = "backtrace-debuginfo-aux.rs"] mod aux;
25
26 macro_rules! pos {
27     () => ((file!(), line!()))
28 }
29
30 macro_rules! dump_and_die {
31     ($($pos:expr),*) => ({
32         // FIXME(#18285): we cannot include the current position because
33         // the macro span takes over the last frame's file/line.
34         if cfg!(any(target_os = "android",
35                     all(target_os = "linux", target_arch = "arm"),
36                     target_os = "freebsd",
37                     target_os = "dragonfly",
38                     target_os = "openbsd")) {
39             // skip these platforms as this support isn't implemented yet.
40         } else {
41             dump_filelines(&[$($pos),*]);
42             panic!();
43         }
44     })
45 }
46
47 // we can't use a function as it will alter the backtrace
48 macro_rules! check {
49     ($counter:expr; $($pos:expr),*) => ({
50         if *$counter == 0 {
51             dump_and_die!($($pos),*)
52         } else {
53             *$counter -= 1;
54         }
55     })
56 }
57
58 type Pos = (&'static str, u32);
59
60 // this goes to stdout and each line has to be occurred
61 // in the following backtrace to stderr with a correct order.
62 fn dump_filelines(filelines: &[Pos]) {
63     for &(file, line) in filelines.iter().rev() {
64         // extract a basename
65         let basename = file.split(&['/', '\\'][..]).last().unwrap();
66         println!("{}:{}", basename, line);
67     }
68 }
69
70 #[inline(never)]
71 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
72     check!(counter; main_pos, outer_pos);
73     check!(counter; main_pos, outer_pos);
74     let inner_pos = pos!(); aux::callback(|aux_pos| {
75         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
76     });
77     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
78         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
79     });
80 }
81
82 // We emit the wrong location for the caller here when inlined on MSVC
83 #[cfg_attr(not(target_env = "msvc"), inline(always))]
84 #[cfg_attr(target_env = "msvc", inline(never))]
85 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
86     check!(counter; main_pos, outer_pos);
87     check!(counter; main_pos, outer_pos);
88
89     // Again, disable inlining for MSVC.
90     #[cfg_attr(not(target_env = "msvc"), inline(always))]
91     #[cfg_attr(target_env = "msvc", inline(never))]
92     fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
93         check!(counter; main_pos, outer_pos, inner_pos);
94     }
95     inner_further_inlined(counter, main_pos, outer_pos, pos!());
96
97     let inner_pos = pos!(); aux::callback(|aux_pos| {
98         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
99     });
100     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
101         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
102     });
103
104     // this tests a distinction between two independent calls to the inlined function.
105     // (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
106     inner_further_inlined(counter, main_pos, outer_pos, pos!());
107 }
108
109 #[inline(never)]
110 fn outer(mut counter: i32, main_pos: Pos) {
111     inner(&mut counter, main_pos, pos!());
112     inner_inlined(&mut counter, main_pos, pos!());
113 }
114
115 fn check_trace(output: &str, error: &str) -> Result<(), String> {
116     // reverse the position list so we can start with the last item (which was the first line)
117     let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect();
118
119     if !error.contains("stack backtrace") {
120         return Err(format!("no backtrace found in stderr:\n{}", error))
121     }
122     for line in error.lines() {
123         if !remaining.is_empty() && line.contains(remaining.last().unwrap()) {
124             remaining.pop();
125         }
126     }
127     if !remaining.is_empty() {
128         return Err(format!("trace does not match position list\n\
129             still need to find {:?}\n\n\
130             --- stdout\n{}\n\
131             --- stderr\n{}",
132             remaining, output, error))
133     }
134     Ok(())
135 }
136
137 fn run_test(me: &str) {
138     use std::str;
139     use std::process::Command;
140
141     let mut i = 0;
142     let mut errors = Vec::new();
143     loop {
144         let out = Command::new(me)
145                           .env("RUST_BACKTRACE", "full")
146                           .arg(i.to_string()).output().unwrap();
147         let output = str::from_utf8(&out.stdout).unwrap();
148         let error = str::from_utf8(&out.stderr).unwrap();
149         if out.status.success() {
150             assert!(output.contains("done."), "bad output for successful run: {}", output);
151             break;
152         } else {
153             if let Err(e) = check_trace(output, error) {
154                 errors.push(e);
155             }
156         }
157         i += 1;
158     }
159     if errors.len() > 0 {
160         for error in errors {
161             println!("---------------------------------------");
162             println!("{}", error);
163         }
164
165         panic!("found some errors");
166     }
167 }
168
169 #[inline(never)]
170 fn main() {
171     let args: Vec<String> = env::args().collect();
172     if args.len() >= 2 {
173         let case = args[1].parse().unwrap();
174         eprintln!("test case {}", case);
175         outer(case, pos!());
176         println!("done.");
177     } else {
178         run_test(&args[0]);
179     }
180 }