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