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