]>
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 | ||
e9174d1e SL |
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. | |
7453a54e | 17 | |
e9174d1e | 18 | // compile-flags:-g -Cllvm-args=-enable-tail-merge=0 |
c30ab7b3 | 19 | // ignore-pretty issue #37195 |
5bcae85e | 20 | // ignore-emscripten spawning processes is not supported |
c34b1796 AL |
21 | |
22 | use std::io; | |
23 | use std::io::prelude::*; | |
24 | use std::env; | |
25 | ||
26 | #[path = "backtrace-debuginfo-aux.rs"] mod aux; | |
27 | ||
28 | macro_rules! pos { | |
29 | () => ((file!(), line!())) | |
30 | } | |
31 | ||
c34b1796 AL |
32 | macro_rules! dump_and_die { |
33 | ($($pos:expr),*) => ({ | |
34 | // FIXME(#18285): we cannot include the current position because | |
35 | // the macro span takes over the last frame's file/line. | |
54a0048b SL |
36 | if cfg!(any(target_os = "macos", |
37 | target_os = "ios", | |
38 | target_os = "android", | |
39 | all(target_os = "linux", target_arch = "arm"), | |
54a0048b SL |
40 | target_os = "freebsd", |
41 | target_os = "dragonfly", | |
42 | target_os = "bitrig", | |
43 | target_os = "openbsd")) { | |
7453a54e SL |
44 | // skip these platforms as this support isn't implemented yet. |
45 | } else { | |
46 | dump_filelines(&[$($pos),*]); | |
47 | panic!(); | |
48 | } | |
c34b1796 AL |
49 | }) |
50 | } | |
51 | ||
c34b1796 AL |
52 | // we can't use a function as it will alter the backtrace |
53 | macro_rules! check { | |
54 | ($counter:expr; $($pos:expr),*) => ({ | |
55 | if *$counter == 0 { | |
56 | dump_and_die!($($pos),*) | |
57 | } else { | |
58 | *$counter -= 1; | |
59 | } | |
60 | }) | |
61 | } | |
62 | ||
63 | type Pos = (&'static str, u32); | |
64 | ||
65 | // this goes to stdout and each line has to be occurred | |
66 | // in the following backtrace to stderr with a correct order. | |
67 | fn dump_filelines(filelines: &[Pos]) { | |
e9174d1e SL |
68 | // Skip top frame for MSVC, because it sees the macro rather than |
69 | // the containing function. | |
70 | let skip = if cfg!(target_env = "msvc") {1} else {0}; | |
71 | for &(file, line) in filelines.iter().rev().skip(skip) { | |
c34b1796 AL |
72 | // extract a basename |
73 | let basename = file.split(&['/', '\\'][..]).last().unwrap(); | |
74 | println!("{}:{}", basename, line); | |
75 | } | |
76 | } | |
77 | ||
78 | #[inline(never)] | |
79 | fn inner(counter: &mut i32, main_pos: Pos, outer_pos: Pos) { | |
80 | check!(counter; main_pos, outer_pos); | |
81 | check!(counter; main_pos, outer_pos); | |
82 | let inner_pos = pos!(); aux::callback(|aux_pos| { | |
83 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
84 | }); | |
85 | let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { | |
86 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
87 | }); | |
88 | } | |
89 | ||
e9174d1e SL |
90 | // LLVM does not yet output the required debug info to support showing inlined |
91 | // function calls in backtraces when targetting MSVC, so disable inlining in | |
92 | // this case. | |
93 | #[cfg_attr(not(target_env = "msvc"), inline(always))] | |
94 | #[cfg_attr(target_env = "msvc", inline(never))] | |
c34b1796 AL |
95 | fn inner_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos) { |
96 | check!(counter; main_pos, outer_pos); | |
97 | check!(counter; main_pos, outer_pos); | |
98 | ||
e9174d1e SL |
99 | // Again, disable inlining for MSVC. |
100 | #[cfg_attr(not(target_env = "msvc"), inline(always))] | |
101 | #[cfg_attr(target_env = "msvc", inline(never))] | |
c34b1796 AL |
102 | fn inner_further_inlined(counter: &mut i32, main_pos: Pos, outer_pos: Pos, inner_pos: Pos) { |
103 | check!(counter; main_pos, outer_pos, inner_pos); | |
104 | } | |
105 | inner_further_inlined(counter, main_pos, outer_pos, pos!()); | |
106 | ||
107 | let inner_pos = pos!(); aux::callback(|aux_pos| { | |
108 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
109 | }); | |
110 | let inner_pos = pos!(); aux::callback_inlined(|aux_pos| { | |
111 | check!(counter; main_pos, outer_pos, inner_pos, aux_pos); | |
112 | }); | |
e9174d1e SL |
113 | |
114 | // this tests a distinction between two independent calls to the inlined function. | |
115 | // (un)fortunately, LLVM somehow merges two consecutive such calls into one node. | |
116 | inner_further_inlined(counter, main_pos, outer_pos, pos!()); | |
c34b1796 AL |
117 | } |
118 | ||
119 | #[inline(never)] | |
120 | fn outer(mut counter: i32, main_pos: Pos) { | |
121 | inner(&mut counter, main_pos, pos!()); | |
122 | inner_inlined(&mut counter, main_pos, pos!()); | |
123 | } | |
124 | ||
125 | fn check_trace(output: &str, error: &str) { | |
126 | // reverse the position list so we can start with the last item (which was the first line) | |
127 | let mut remaining: Vec<&str> = output.lines().map(|s| s.trim()).rev().collect(); | |
128 | ||
129 | assert!(error.contains("stack backtrace"), "no backtrace in the error: {}", error); | |
130 | for line in error.lines() { | |
131 | if !remaining.is_empty() && line.contains(remaining.last().unwrap()) { | |
132 | remaining.pop(); | |
133 | } | |
134 | } | |
135 | assert!(remaining.is_empty(), | |
136 | "trace does not match position list: {}\n---\n{}", error, output); | |
137 | } | |
138 | ||
139 | fn run_test(me: &str) { | |
140 | use std::str; | |
141 | use std::process::Command; | |
142 | ||
143 | let mut template = Command::new(me); | |
8bb4bdeb | 144 | template.env("RUST_BACKTRACE", "full"); |
c34b1796 AL |
145 | |
146 | let mut i = 0; | |
147 | loop { | |
148 | let out = Command::new(me) | |
8bb4bdeb | 149 | .env("RUST_BACKTRACE", "full") |
c34b1796 AL |
150 | .arg(i.to_string()).output().unwrap(); |
151 | let output = str::from_utf8(&out.stdout).unwrap(); | |
152 | let error = str::from_utf8(&out.stderr).unwrap(); | |
153 | if out.status.success() { | |
154 | assert!(output.contains("done."), "bad output for successful run: {}", output); | |
155 | break; | |
156 | } else { | |
157 | check_trace(output, error); | |
158 | } | |
159 | i += 1; | |
160 | } | |
161 | } | |
162 | ||
163 | #[inline(never)] | |
164 | fn main() { | |
165 | let args: Vec<String> = env::args().collect(); | |
166 | if args.len() >= 2 { | |
167 | let case = args[1].parse().unwrap(); | |
168 | writeln!(&mut io::stderr(), "test case {}", case).unwrap(); | |
169 | outer(case, pos!()); | |
170 | println!("done."); | |
171 | } else { | |
172 | run_test(&args[0]); | |
173 | } | |
174 | } |