]> git.proxmox.com Git - rustc.git/blob - src/test/run-pass/backtrace-debuginfo.rs
Imported Upstream version 1.9.0+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 #![feature(rustc_attrs)]
12
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.
19
20 // compile-flags:-g -Cllvm-args=-enable-tail-merge=0
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
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.
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")) {
46 // skip these platforms as this support isn't implemented yet.
47 } else {
48 dump_filelines(&[$($pos),*]);
49 panic!();
50 }
51 })
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 #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
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
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))]
98 #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
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
103 // Again, disable inlining for MSVC.
104 #[cfg_attr(not(target_env = "msvc"), inline(always))]
105 #[cfg_attr(target_env = "msvc", inline(never))]
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 });
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!());
121 }
122
123 #[inline(never)]
124 #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
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)]
169 #[rustc_no_mir] // FIXME #31005 MIR missing debuginfo currently.
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 }
181