1 extern crate backtrace;
2 extern crate findshlibs;
3 extern crate rustc_test as test;
4 
5 use std::env;
6 use std::ffi::OsStr;
7 use std::path::Path;
8 use std::process::Command;
9 
10 use backtrace::Backtrace;
11 use findshlibs::{IterationControl, SharedLibrary, TargetSharedLibrary};
12 use test::{ShouldPanic, TestDesc, TestDescAndFn, TestFn, TestName};
13 
make_trace() -> Vec<String>14 fn make_trace() -> Vec<String> {
15     fn foo() -> Backtrace {
16         bar()
17     }
18     #[inline(never)]
19     fn bar() -> Backtrace {
20         baz()
21     }
22     #[inline(always)]
23     fn baz() -> Backtrace {
24         Backtrace::new_unresolved()
25     }
26 
27     let mut base_addr = None;
28     TargetSharedLibrary::each(|lib| {
29         base_addr = Some(lib.virtual_memory_bias().0 as isize);
30         IterationControl::Break
31     });
32     let addrfix = -base_addr.unwrap();
33 
34     let trace = foo();
35     trace
36         .frames()
37         .iter()
38         .take(5)
39         .map(|x| format!("{:p}", (x.ip() as *const u8).wrapping_offset(addrfix)))
40         .collect()
41 }
42 
run_cmd<P: AsRef<OsStr>>(exe: P, me: &Path, flags: Option<&str>, trace: &str) -> String43 fn run_cmd<P: AsRef<OsStr>>(exe: P, me: &Path, flags: Option<&str>, trace: &str) -> String {
44     let mut cmd = Command::new(exe);
45     cmd.env("LC_ALL", "C"); // GNU addr2line is localized, we aren't
46     cmd.env("RUST_BACKTRACE", "1"); // if a child crashes, we want to know why
47 
48     if let Some(flags) = flags {
49         cmd.arg(flags);
50     }
51     cmd.arg("--exe").arg(me).arg(trace);
52 
53     let output = cmd.output().unwrap();
54 
55     assert!(output.status.success());
56     String::from_utf8(output.stdout).unwrap()
57 }
58 
run_test(flags: Option<&str>)59 fn run_test(flags: Option<&str>) {
60     let me = env::current_exe().unwrap();
61     let mut exe = me.clone();
62     assert!(exe.pop());
63     if exe.file_name().unwrap().to_str().unwrap() == "deps" {
64         assert!(exe.pop());
65     }
66     exe.push("examples");
67     exe.push("addr2line");
68 
69     assert!(exe.is_file());
70 
71     let trace = make_trace();
72 
73     // HACK: GNU addr2line has a bug where looking up multiple addresses can cause the second
74     // lookup to fail. Workaround by doing one address at a time.
75     for addr in &trace {
76         let theirs = run_cmd("addr2line", &me, flags, addr);
77         let ours = run_cmd(&exe, &me, flags, addr);
78 
79         // HACK: GNU addr2line does not tidy up paths properly, causing double slashes to be printed
80         //       for for inlined frames from /src/libpanic_unwind/lib.rs
81         // We consider our behavior to be correct, so we fix their output to match ours.
82         let theirs = theirs.replace("//src", "/src");
83 
84         assert!(
85             theirs == ours,
86             "Output not equivalent:
87 
88 $ addr2line {0} --exe {1} {2}
89 {4}
90 $ {3} {0} --exe {1} {2}
91 {5}
92 
93 
94 ",
95             flags.unwrap_or(""),
96             me.display(),
97             trace.join(" "),
98             exe.display(),
99             theirs,
100             ours
101         );
102     }
103 }
104 
105 static FLAGS: &'static str = "aipsf";
106 
make_tests() -> Vec<TestDescAndFn>107 fn make_tests() -> Vec<TestDescAndFn> {
108     (0..(1 << FLAGS.len()))
109         .map(|bits| {
110             if bits == 0 {
111                 None
112             } else {
113                 let mut param = String::new();
114                 param.push('-');
115                 for (i, flag) in FLAGS.chars().enumerate() {
116                     if (bits & (1 << i)) != 0 {
117                         param.push(flag);
118                     }
119                 }
120                 Some(param)
121             }
122         })
123         .map(|param| TestDescAndFn {
124             desc: TestDesc {
125                 name: TestName::DynTestName(format!(
126                     "addr2line {}",
127                     param.as_ref().map_or("", String::as_str)
128                 )),
129                 ignore: false,
130                 should_panic: ShouldPanic::No,
131                 allow_fail: false,
132             },
133             testfn: TestFn::DynTestFn(Box::new(move || {
134                 run_test(param.as_ref().map(String::as_str))
135             })),
136         })
137         .collect()
138 }
139 
main()140 fn main() {
141     if !cfg!(target_os = "linux") {
142         return;
143     }
144     let args: Vec<_> = env::args().collect();
145     test::test_main(&args, make_tests());
146 }
147