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