]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
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 | ||
54a0048b SL |
11 | #![feature(rustc_attrs)] |
12 | ||
e9174d1e SL |
13 | // We disable tail merging here because it can't preserve debuginfo and thus |
14 | // potentially breaks the backtraces. Also, subtle changes can decide whether | |
15 | // tail merging suceeds, so the test might work today but fail tomorrow due to a | |
16 | // seemingly completely unrelated change. | |
17 | // Unfortunately, LLVM has no "disable" option for this, so we have to set | |
18 | // "enable" to 0 instead. | |
7453a54e | 19 | |
e9174d1e | 20 | // compile-flags:-g -Cllvm-args=-enable-tail-merge=0 |
c34b1796 AL |
21 | // ignore-pretty as this critically relies on line numbers |
22 | ||
23 | use std::io; | |
24 | use std::io::prelude::*; | |
25 | use std::env; | |
26 | ||
27 | #[path = "backtrace-debuginfo-aux.rs"] mod aux; | |
28 | ||
29 | macro_rules! pos { | |
30 | () => ((file!(), line!())) | |
31 | } | |
32 | ||
c34b1796 AL |
33 | macro_rules! dump_and_die { |
34 | ($($pos:expr),*) => ({ | |
35 | // FIXME(#18285): we cannot include the current position because | |
36 | // the macro span takes over the last frame's file/line. | |
54a0048b SL |
37 | if cfg!(any(target_os = "macos", |
38 | target_os = "ios", | |
39 | target_os = "android", | |
40 | all(target_os = "linux", target_arch = "arm"), | |
41 | target_os = "windows", | |
42 | target_os = "freebsd", | |
43 | target_os = "dragonfly", | |
44 | target_os = "bitrig", | |
45 | target_os = "openbsd")) { | |
7453a54e SL |
46 | // skip these platforms as this support isn't implemented yet. |
47 | } else { | |
48 | dump_filelines(&[$($pos),*]); | |
49 | panic!(); | |
50 | } | |
c34b1796 AL |
51 | }) |
52 | } | |
53 | ||
c34b1796 AL |
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]) { | |
e9174d1e SL |
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) { | |
c34b1796 AL |
74 | // extract a basename |
75 | let basename = file.split(&['/', '\\'][..]).last().unwrap(); | |
76 | println!("{}:{}", basename, line); | |
77 | } | |
78 | } | |
79 | ||
80 | #[inline(never)] | |
54a0048b | 81 | #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently. |
c34b1796 AL |
82 | fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) { |
83 | check!(counter; main_pos, outer_pos); | |
84 | check!(counter; main_pos, outer_pos); | |
85 | let inner_pos = pos!(); aux::callback(|aux_pos| { | |
86 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
87 | }); | |
88 | let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { | |
89 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
90 | }); | |
91 | } | |
92 | ||
e9174d1e SL |
93 | // LLVM does not yet output the required debug info to support showing inlined |
94 | // function calls in backtraces when targetting MSVC, so disable inlining in | |
95 | // this case. | |
96 | #[cfg_attr(not(target_env = "msvc"), inline(always))] | |
97 | #[cfg_attr(target_env = "msvc", inline(never))] | |
54a0048b | 98 | #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently. |
c34b1796 AL |
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 | ||
e9174d1e SL |
103 | // Again, disable inlining for MSVC. |
104 | #[cfg_attr(not(target_env = "msvc"), inline(always))] | |
105 | #[cfg_attr(target_env = "msvc", inline(never))] | |
c34b1796 AL |
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 | }); | |
e9174d1e SL |
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!()); | |
c34b1796 AL |
121 | } |
122 | ||
123 | #[inline(never)] | |
54a0048b | 124 | #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently. |
c34b1796 AL |
125 | fn outer(mut counter: i32, main_pos: Pos) { |
126 | inner(&mut counter, main_pos, pos!()); | |
127 | inner_inlined(&mut counter, main_pos, pos!()); | |
128 | } | |
129 | ||
130 | fn check_trace(output: &str, error: &str) { | |
131 | // reverse the position list so we can start with the last item (which was the first line) | |
132 | let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect(); | |
133 | ||
134 | assert!(error.contains("stack backtrace"), "no backtrace in the error: {}", error); | |
135 | for line in error.lines() { | |
136 | if !remaining.is_empty() && line.contains(remaining.last().unwrap()) { | |
137 | remaining.pop(); | |
138 | } | |
139 | } | |
140 | assert!(remaining.is_empty(), | |
141 | "trace does not match position list: {}\n---\n{}", error, output); | |
142 | } | |
143 | ||
144 | fn run_test(me: &str) { | |
145 | use std::str; | |
146 | use std::process::Command; | |
147 | ||
148 | let mut template = Command::new(me); | |
149 | template.env("RUST_BACKTRACE", "1"); | |
150 | ||
151 | let mut i = 0; | |
152 | loop { | |
153 | let out = Command::new(me) | |
154 | .env("RUST_BACKTRACE", "1") | |
155 | .arg(i.to_string()).output().unwrap(); | |
156 | let output = str::from_utf8(&out.stdout).unwrap(); | |
157 | let error = str::from_utf8(&out.stderr).unwrap(); | |
158 | if out.status.success() { | |
159 | assert!(output.contains("done."), "bad output for successful run: {}", output); | |
160 | break; | |
161 | } else { | |
162 | check_trace(output, error); | |
163 | } | |
164 | i += 1; | |
165 | } | |
166 | } | |
167 | ||
168 | #[inline(never)] | |
54a0048b | 169 | #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently. |
c34b1796 AL |
170 | fn main() { |
171 | let args: Vec<String> = env::args().collect(); | |
172 | if args.len() >= 2 { | |
173 | let case = args[1].parse().unwrap(); | |
174 | writeln!(&mut io::stderr(), "test case {}", case).unwrap(); | |
175 | outer(case, pos!()); | |
176 | println!("done."); | |
177 | } else { | |
178 | run_test(&args[0]); | |
179 | } | |
180 | } | |
e9174d1e | 181 |