]>
Commit | Line | Data |
---|---|---|
064997fb FG |
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 | ||
9ffffee4 | 14 | #[inline(never)] |
064997fb FG |
15 | fn make_trace() -> Vec<String> { |
16 | fn foo() -> Backtrace { | |
17 | bar() | |
18 | } | |
19 | #[inline(never)] | |
20 | fn bar() -> Backtrace { | |
21 | baz() | |
22 | } | |
23 | #[inline(always)] | |
24 | fn baz() -> Backtrace { | |
25 | Backtrace::new_unresolved() | |
26 | } | |
27 | ||
28 | let mut base_addr = None; | |
29 | TargetSharedLibrary::each(|lib| { | |
30 | base_addr = Some(lib.virtual_memory_bias().0 as isize); | |
31 | IterationControl::Break | |
32 | }); | |
33 | let addrfix = -base_addr.unwrap(); | |
34 | ||
35 | let trace = foo(); | |
36 | trace | |
37 | .frames() | |
38 | .iter() | |
39 | .take(5) | |
40 | .map(|x| format!("{:p}", (x.ip() as *const u8).wrapping_offset(addrfix))) | |
41 | .collect() | |
42 | } | |
43 | ||
44 | fn run_cmd<P: AsRef<OsStr>>(exe: P, me: &Path, flags: Option<&str>, trace: &str) -> String { | |
45 | let mut cmd = Command::new(exe); | |
46 | cmd.env("LC_ALL", "C"); // GNU addr2line is localized, we aren't | |
47 | cmd.env("RUST_BACKTRACE", "1"); // if a child crashes, we want to know why | |
48 | ||
49 | if let Some(flags) = flags { | |
50 | cmd.arg(flags); | |
51 | } | |
52 | cmd.arg("--exe").arg(me).arg(trace); | |
53 | ||
54 | let output = cmd.output().unwrap(); | |
55 | ||
56 | assert!(output.status.success()); | |
57 | String::from_utf8(output.stdout).unwrap() | |
58 | } | |
59 | ||
60 | fn run_test(flags: Option<&str>) { | |
61 | let me = env::current_exe().unwrap(); | |
62 | let mut exe = me.clone(); | |
63 | assert!(exe.pop()); | |
64 | if exe.file_name().unwrap().to_str().unwrap() == "deps" { | |
65 | assert!(exe.pop()); | |
66 | } | |
67 | exe.push("examples"); | |
68 | exe.push("addr2line"); | |
69 | ||
70 | assert!(exe.is_file()); | |
71 | ||
72 | let trace = make_trace(); | |
73 | ||
74 | // HACK: GNU addr2line has a bug where looking up multiple addresses can cause the second | |
75 | // lookup to fail. Workaround by doing one address at a time. | |
76 | for addr in &trace { | |
77 | let theirs = run_cmd("addr2line", &me, flags, addr); | |
78 | let ours = run_cmd(&exe, &me, flags, addr); | |
79 | ||
80 | // HACK: GNU addr2line does not tidy up paths properly, causing double slashes to be printed. | |
81 | // We consider our behavior to be correct, so we fix their output to match ours. | |
82 | let theirs = theirs.replace("//", "/"); | |
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 | ||
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 | ||
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 | } |