]> git.lizzy.rs Git - rust.git/blob - src/test/run-pass/backtrace-debuginfo.rs
Get tests working on MSVC 32-bit
[rust.git] / src / test / run-pass / backtrace-debuginfo.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // We disable tail merging here because it can't preserve debuginfo and thus
12 // potentially breaks the backtraces. Also, subtle changes can decide whether
13 // tail merging suceeds, so the test might work today but fail tomorrow due to a
14 // seemingly completely unrelated change.
15 // Unfortunately, LLVM has no "disable" option for this, so we have to set
16 // "enable" to 0 instead.
17 // compile-flags:-g -Cllvm-args=-enable-tail-merge=0
18 // ignore-pretty as this critically relies on line numbers
19
20 use std::io;
21 use std::io::prelude::*;
22 use std::env;
23
24 #[path = "backtrace-debuginfo-aux.rs"] mod aux;
25
26 macro_rules! pos {
27     () => ((file!(), line!()))
28 }
29
30 #[cfg(all(unix,
31           not(target_os = "macos"),
32           not(target_os = "ios"),
33           not(target_os = "android"),
34           not(all(target_os = "linux", target_arch = "arm"))))]
35 macro_rules! dump_and_die {
36     ($($pos:expr),*) => ({
37         // FIXME(#18285): we cannot include the current position because
38         // the macro span takes over the last frame's file/line.
39         dump_filelines(&[$($pos),*]);
40         panic!();
41     })
42 }
43
44 // this does not work on Windows, Android, OSX or iOS
45 #[cfg(not(all(unix,
46               not(target_os = "macos"),
47               not(target_os = "ios"),
48               not(target_os = "android"),
49               not(all(target_os = "linux", target_arch = "arm")))))]
50 macro_rules! dump_and_die {
51     ($($pos:expr),*) => ({ let _ = [$($pos),*]; })
52 }
53
54 // we can't use a function as it will alter the backtrace
55 macro_rules! check {
56     ($counter:expr; $($pos:expr),*) => ({
57         if *$counter == 0 {
58             dump_and_die!($($pos),*)
59         } else {
60             *$counter -= 1;
61         }
62     })
63 }
64
65 type Pos = (&'static str, u32);
66
67 // this goes to stdout and each line has to be occurred
68 // in the following backtrace to stderr with a correct order.
69 fn dump_filelines(filelines: &[Pos]) {
70     // Skip top frame for MSVC, because it sees the macro rather than
71     // the containing function.
72     let skip = if cfg!(target_env = "msvc") {1} else {0};
73     for &(file, line) in filelines.iter().rev().skip(skip) {
74         // extract a basename
75         let basename = file.split(&['/', '\\'][..]).last().unwrap();
76         println!("{}:{}", basename, line);
77     }
78 }
79
80 #[inline(never)]
81 fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
82     check!(counter; main_pos, outer_pos);
83     check!(counter; main_pos, outer_pos);
84     let inner_pos = pos!(); aux::callback(|aux_pos| {
85         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
86     });
87     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
88         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
89     });
90 }
91
92 // LLVM does not yet output the required debug info to support showing inlined
93 // function calls in backtraces when targetting MSVC, so disable inlining in
94 // this case.
95 #[cfg_attr(not(target_env = "msvc"), inline(always))]
96 #[cfg_attr(target_env = "msvc", inline(never))]
97 fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) {
98     check!(counter; main_pos, outer_pos);
99     check!(counter; main_pos, outer_pos);
100
101     // Again, disable inlining for MSVC.
102     #[cfg_attr(not(target_env = "msvc"), inline(always))]
103     #[cfg_attr(target_env = "msvc", inline(never))]
104     fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) {
105         check!(counter; main_pos, outer_pos, inner_pos);
106     }
107     inner_further_inlined(counter, main_pos, outer_pos, pos!());
108
109     let inner_pos = pos!(); aux::callback(|aux_pos| {
110         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
111     });
112     let inner_pos = pos!(); aux::callback_inlined(|aux_pos| {
113         check!(counter; main_pos, outer_pos, inner_pos, aux_pos);
114     });
115
116     // this tests a distinction between two independent calls to the inlined function.
117     // (un)fortunately, LLVM somehow merges two consecutive such calls into one node.
118     inner_further_inlined(counter, main_pos, outer_pos, pos!());
119 }
120
121 #[inline(never)]
122 fn outer(mut counter: i32, main_pos: Pos) {
123     inner(&mut counter, main_pos, pos!());
124     inner_inlined(&mut counter, main_pos, pos!());
125 }
126
127 fn check_trace(output: &str, error: &str) {
128     // reverse the position list so we can start with the last item (which was the first line)
129     let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect();
130
131     assert!(error.contains("stack backtrace"), "no backtrace in the error: {}", error);
132     for line in error.lines() {
133         if !remaining.is_empty() && line.contains(remaining.last().unwrap()) {
134             remaining.pop();
135         }
136     }
137     assert!(remaining.is_empty(),
138             "trace does not match position list: {}\n---\n{}", error, output);
139 }
140
141 fn run_test(me: &str) {
142     use std::str;
143     use std::process::Command;
144
145     let mut template = Command::new(me);
146     template.env("RUST_BACKTRACE", "1");
147
148     let mut i = 0;
149     loop {
150         let out = Command::new(me)
151                           .env("RUST_BACKTRACE", "1")
152                           .arg(i.to_string()).output().unwrap();
153         let output = str::from_utf8(&out.stdout).unwrap();
154         let error = str::from_utf8(&out.stderr).unwrap();
155         if out.status.success() {
156             assert!(output.contains("done."), "bad output for successful run: {}", output);
157             break;
158         } else {
159             check_trace(output, error);
160         }
161         i += 1;
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         writeln!(&mut io::stderr(), "test case {}", case).unwrap();
171         outer(case, pos!());
172         println!("done.");
173     } else {
174         run_test(&args[0]);
175     }
176 }
177