]>
Commit | Line | Data |
---|---|---|
416331ca | 1 | // run-pass |
e9174d1e SL |
2 | // We disable tail merging here because it can't preserve debuginfo and thus |
3 | // potentially breaks the backtraces. Also, subtle changes can decide whether | |
3b2f2976 | 4 | // tail merging succeeds, so the test might work today but fail tomorrow due to a |
e9174d1e SL |
5 | // seemingly completely unrelated change. |
6 | // Unfortunately, LLVM has no "disable" option for this, so we have to set | |
7 | // "enable" to 0 instead. | |
7453a54e | 8 | |
17df50a5 | 9 | // compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0 |
83c7162d | 10 | // compile-flags:-Cforce-frame-pointers=yes |
064997fb | 11 | // compile-flags:-Cstrip=none |
c30ab7b3 | 12 | // ignore-pretty issue #37195 |
5bcae85e | 13 | // ignore-emscripten spawning processes is not supported |
48663c56 | 14 | // ignore-sgx no processes |
2b03887a | 15 | // ignore-fuchsia Backtrace not symbolized, trace different line alignment |
2c00a5a8 | 16 | |
c34b1796 AL |
17 | use std::env; |
18 | ||
19 | #[path = "backtrace-debuginfo-aux.rs"] mod aux; | |
20 | ||
21 | macro_rules! pos { | |
22 | () => ((file!(), line!())) | |
23 | } | |
24 | ||
c34b1796 AL |
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. | |
416331ca XL |
29 | // |
30 | // You might also be wondering why a major platform, | |
31 | // i686-pc-windows-msvc, is located in here. Some of the saga can be | |
32 | // found on #62897, but the tl;dr; is that it appears that if the | |
33 | // standard library doesn't have debug information or frame pointers, | |
34 | // which it doesn't by default on the test builders, then the stack | |
35 | // walking routines in dbghelp will randomly terminate the stack trace | |
36 | // in libstd without going further. Presumably the addition of frame | |
37 | // pointers and/or debuginfo fixes this since tests always work with | |
38 | // nightly compilers (which have debuginfo). In general though this test | |
39 | // is replicated in rust-lang/backtrace-rs and has extensive coverage | |
40 | // there, even on i686-pc-windows-msvc. We do the best we can in | |
41 | // rust-lang/rust to test it as well, but sometimes we just gotta keep | |
42 | // landing PRs. | |
ea8adc8c | 43 | if cfg!(any(target_os = "android", |
54a0048b | 44 | all(target_os = "linux", target_arch = "arm"), |
416331ca | 45 | all(target_env = "msvc", target_arch = "x86"), |
54a0048b SL |
46 | target_os = "freebsd", |
47 | target_os = "dragonfly", | |
54a0048b | 48 | target_os = "openbsd")) { |
7453a54e SL |
49 | // skip these platforms as this support isn't implemented yet. |
50 | } else { | |
51 | dump_filelines(&[$($pos),*]); | |
52 | panic!(); | |
53 | } | |
c34b1796 AL |
54 | }) |
55 | } | |
56 | ||
c34b1796 AL |
57 | // we can't use a function as it will alter the backtrace |
58 | macro_rules! check { | |
59 | ($counter:expr; $($pos:expr),*) => ({ | |
60 | if *$counter == 0 { | |
61 | dump_and_die!($($pos),*) | |
62 | } else { | |
63 | *$counter -= 1; | |
64 | } | |
65 | }) | |
66 | } | |
67 | ||
68 | type Pos = (&'static str, u32); | |
69 | ||
70 | // this goes to stdout and each line has to be occurred | |
71 | // in the following backtrace to stderr with a correct order. | |
72 | fn dump_filelines(filelines: &[Pos]) { | |
2c00a5a8 | 73 | for &(file, line) in filelines.iter().rev() { |
c34b1796 AL |
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 | ||
2c00a5a8 | 92 | // We emit the wrong location for the caller here when inlined on MSVC |
e9174d1e SL |
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 | ||
2c00a5a8 | 125 | fn check_trace(output: &str, error: &str) -> Result<(), String> { |
c34b1796 AL |
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 | ||
2c00a5a8 XL |
129 | if !error.contains("stack backtrace") { |
130 | return Err(format!("no backtrace found in stderr:\n{}", error)) | |
131 | } | |
c34b1796 AL |
132 | for line in error.lines() { |
133 | if !remaining.is_empty() && line.contains(remaining.last().unwrap()) { | |
134 | remaining.pop(); | |
135 | } | |
136 | } | |
2c00a5a8 XL |
137 | if !remaining.is_empty() { |
138 | return Err(format!("trace does not match position list\n\ | |
139 | still need to find {:?}\n\n\ | |
140 | --- stdout\n{}\n\ | |
141 | --- stderr\n{}", | |
142 | remaining, output, error)) | |
143 | } | |
144 | Ok(()) | |
c34b1796 AL |
145 | } |
146 | ||
147 | fn run_test(me: &str) { | |
148 | use std::str; | |
149 | use std::process::Command; | |
150 | ||
c34b1796 | 151 | let mut i = 0; |
2c00a5a8 | 152 | let mut errors = Vec::new(); |
c34b1796 AL |
153 | loop { |
154 | let out = Command::new(me) | |
8bb4bdeb | 155 | .env("RUST_BACKTRACE", "full") |
c34b1796 AL |
156 | .arg(i.to_string()).output().unwrap(); |
157 | let output = str::from_utf8(&out.stdout).unwrap(); | |
158 | let error = str::from_utf8(&out.stderr).unwrap(); | |
159 | if out.status.success() { | |
160 | assert!(output.contains("done."), "bad output for successful run: {}", output); | |
161 | break; | |
162 | } else { | |
2c00a5a8 XL |
163 | if let Err(e) = check_trace(output, error) { |
164 | errors.push(e); | |
165 | } | |
c34b1796 AL |
166 | } |
167 | i += 1; | |
168 | } | |
2c00a5a8 XL |
169 | if errors.len() > 0 { |
170 | for error in errors { | |
171 | println!("---------------------------------------"); | |
172 | println!("{}", error); | |
173 | } | |
174 | ||
175 | panic!("found some errors"); | |
176 | } | |
c34b1796 AL |
177 | } |
178 | ||
179 | #[inline(never)] | |
180 | fn main() { | |
181 | let args: Vec<String> = env::args().collect(); | |
182 | if args.len() >= 2 { | |
183 | let case = args[1].parse().unwrap(); | |
abe05a73 | 184 | eprintln!("test case {}", case); |
c34b1796 AL |
185 | outer(case, pos!()); |
186 | println!("done."); | |
187 | } else { | |
188 | run_test(&args[0]); | |
189 | } | |
190 | } |