]> git.proxmox.com Git - rustc.git/blame - src/test/run-pass/backtrace-debuginfo.rs
New upstream version 1.19.0+dfsg1
[rustc.git] / src / test / run-pass / backtrace-debuginfo.rs
CommitLineData
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
22use std::io;
23use std::io::prelude::*;
24use std::env;
25
26#[path = "backtrace-debuginfo-aux.rs"] mod aux;
27
28macro_rules! pos {
29 () => ((file!(), line!()))
30}
31
c34b1796
AL
32macro_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
53macro_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
63type 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.
67fn 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)]
79fn 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
95fn 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)]
120fn outer(mut counter: i32, main_pos: Pos) {
121 inner(&mut counter, main_pos, pos!());
122 inner_inlined(&mut counter, main_pos, pos!());
123}
124
125fn 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
139fn 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)]
164fn 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}