]> git.lizzy.rs Git - rust.git/blob - src/test/ui/backtrace-debuginfo.rs
Rollup merge of #63202 - exphp-forks:parser-ice-63135, r=estebank
[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-sgx no processes
15 // normalize-stderr-test ".*\n" -> ""
16
17 // Note that above `-opt-bisect-limit=0` is used to basically disable
18 // optimizations. It creates tons of output on stderr, hence we normalize
19 // that away entirely.
20
21 use std::env;
22
23 #[path = "backtrace-debuginfo-aux.rs"] mod aux;
24
25 macro_rules! pos {
26     () => ((file!(), line!()))
27 }
28
29 macro_rules! dump_and_die {
30     ($($pos:expr),*) => ({
31         // FIXME(#18285): we cannot include the current position because
32         // the macro span takes over the last frame's file/line.
33         //
34         // You might also be wondering why a major platform,
35         // i686-pc-windows-msvc, is located in here. Some of the saga can be
36         // found on #62897, but the tl;dr; is that it appears that if the
37         // standard library doesn't have debug information or frame pointers,
38         // which it doesn't by default on the test builders, then the stack
39         // walking routines in dbghelp will randomly terminate the stack trace
40         // in libstd without going further. Presumably the addition of frame
41         // pointers and/or debuginfo fixes this since tests always work with
42         // nightly compilers (which have debuginfo). In general though this test
43         // is replicated in rust-lang/backtrace-rs and has extensive coverage
44         // there, even on i686-pc-windows-msvc. We do the best we can in
45         // rust-lang/rust to test it as well, but sometimes we just gotta keep
46         // landing PRs.
47         if cfg!(any(target_os = "android",
48                     all(target_os = "linux", target_arch = "arm"),
49                     all(target_env = "msvc", target_arch = "x86"),
50                     target_os = "freebsd",
51                     target_os = "dragonfly",
52                     target_os = "openbsd")) {
53             // skip these platforms as this support isn't implemented yet.
54         } else {
55             dump_filelines(&[$($pos),*]);
56             panic!();
57         }
58     })
59 }
60
61 // we can't use a function as it will alter the backtrace
62 macro_rules! check {
63     ($counter:expr; $($pos:expr),*) => ({
64         if *$counter == 0 {
65             dump_and_die!($($pos),*)
66         } else {
67             *$counter -= 1;
68         }
69     })
70 }
71
72 type Pos = (&'static str, u32);
73
74 // this goes to stdout and each line has to be occurred
75 // in the following backtrace to stderr with a correct order.
76 fn dump_filelines(filelines: &[Pos]) {
77     for &(file, line) in filelines.iter().rev() {
78         // extract a basename
79         let basename = file.split(&['/', '\\'][..]).last().unwrap();
80         println!("{}:{}", basename, line);
81     }
82 }
83
84 #[inline(never)]
85 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
86     check!(counter; main_pos, outer_pos);
87     check!(counter; main_pos, outer_pos);
88     let inner_pos = pos!(); aux::callback(|aux_pos| {
89         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
90     });
91     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
92         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
93     });
94 }
95
96 // We emit the wrong location for the caller here when inlined on MSVC
97 #[cfg_attr(not(target_env = "msvc"), inline(always))]
98 #[cfg_attr(target_env = "msvc", inline(never))]
99 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
100     check!(counter; main_pos, outer_pos);
101     check!(counter; main_pos, outer_pos);
102
103     // Again, disable inlining for MSVC.
104     #[cfg_attr(not(target_env = "msvc"), inline(always))]
105     #[cfg_attr(target_env = "msvc", inline(never))]
106     fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
107         check!(counter; main_pos, outer_pos, inner_pos);
108     }
109     inner_further_inlined(counter, main_pos, outer_pos, pos!());
110
111     let inner_pos = pos!(); aux::callback(|aux_pos| {
112         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
113     });
114     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
115         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
116     });
117
118     // this tests a distinction between two independent calls to the inlined function.
119     // (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
120     inner_further_inlined(counter, main_pos, outer_pos, pos!());
121 }
122
123 #[inline(never)]
124 fn outer(mut counter: i32, main_pos: Pos) {
125     inner(&mut counter, main_pos, pos!());
126     inner_inlined(&mut counter, main_pos, pos!());
127 }
128
129 fn check_trace(output: &str, error: &str) -> Result<(), String> {
130     // reverse the position list so we can start with the last item (which was the first line)
131     let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect();
132
133     if !error.contains("stack backtrace") {
134         return Err(format!("no backtrace found in stderr:\n{}", error))
135     }
136     for line in error.lines() {
137         if !remaining.is_empty() && line.contains(remaining.last().unwrap()) {
138             remaining.pop();
139         }
140     }
141     if !remaining.is_empty() {
142         return Err(format!("trace does not match position list\n\
143             still need to find {:?}\n\n\
144             --- stdout\n{}\n\
145             --- stderr\n{}",
146             remaining, output, error))
147     }
148     Ok(())
149 }
150
151 fn run_test(me: &str) {
152     use std::str;
153     use std::process::Command;
154
155     let mut i = 0;
156     let mut errors = Vec::new();
157     loop {
158         let out = Command::new(me)
159                           .env("RUST_BACKTRACE", "full")
160                           .arg(i.to_string()).output().unwrap();
161         let output = str::from_utf8(&out.stdout).unwrap();
162         let error = str::from_utf8(&out.stderr).unwrap();
163         if out.status.success() {
164             assert!(output.contains("done."), "bad output for successful run: {}", output);
165             break;
166         } else {
167             if let Err(e) = check_trace(output, error) {
168                 errors.push(e);
169             }
170         }
171         i += 1;
172     }
173     if errors.len() > 0 {
174         for error in errors {
175             println!("---------------------------------------");
176             println!("{}", error);
177         }
178
179         panic!("found some errors");
180     }
181 }
182
183 #[inline(never)]
184 fn main() {
185     let args: Vec<String> = env::args().collect();
186     if args.len() >= 2 {
187         let case = args[1].parse().unwrap();
188         eprintln!("test case {}", case);
189         outer(case, pos!());
190         println!("done.");
191     } else {
192         run_test(&args[0]);
193     }
194 }