]>
Commit | Line | Data |
---|---|---|
7cac9316 XL |
1 | extern crate backtrace; |
2 | ||
3 | use std::os::raw::c_void; | |
4 | use std::thread; | |
5 | ||
6 | static LIBUNWIND: bool = cfg!(all(unix, feature = "libunwind")); | |
7 | static UNIX_BACKTRACE: bool = cfg!(all(unix, feature = "unix-backtrace")); | |
8 | static LIBBACKTRACE: bool = cfg!(all(unix, feature = "libbacktrace")) && | |
abe05a73 XL |
9 | !cfg!(target_os = "fuchsia") && !cfg!(target_os = "macos") && |
10 | !cfg!(target_os = "ios"); | |
041b39d2 | 11 | static CORESYMBOLICATION: bool = cfg!(all(any(target_os = "macos", target_os = "ios"), |
7cac9316 | 12 | feature = "coresymbolication")); |
abe05a73 | 13 | static DLADDR: bool = cfg!(all(unix, feature = "dladdr")) && !cfg!(target_os = "fuchsia"); |
7cac9316 XL |
14 | static DBGHELP: bool = cfg!(all(windows, feature = "dbghelp")); |
15 | static MSVC: bool = cfg!(target_env = "msvc"); | |
ea8adc8c XL |
16 | static GIMLI_SYMBOLIZE: bool = cfg!(all(feature = "gimli-symbolize", |
17 | unix, | |
18 | target_os = "linux")); | |
7cac9316 XL |
19 | |
20 | #[test] | |
21 | fn smoke_test_frames() { | |
22 | frame_1(line!()); | |
23 | #[inline(never)] fn frame_1(start_line: u32) { frame_2(start_line) } | |
24 | #[inline(never)] fn frame_2(start_line: u32) { frame_3(start_line) } | |
25 | #[inline(never)] fn frame_3(start_line: u32) { frame_4(start_line) } | |
26 | #[inline(never)] fn frame_4(start_line: u32) { | |
27 | let mut v = Vec::new(); | |
28 | backtrace::trace(|cx| { | |
29 | v.push((cx.ip(), cx.symbol_address())); | |
30 | true | |
31 | }); | |
32 | ||
33 | if v.len() < 5 { | |
34 | assert!(!LIBUNWIND); | |
35 | assert!(!UNIX_BACKTRACE); | |
36 | assert!(!DBGHELP); | |
37 | return | |
38 | } | |
39 | ||
40 | // On 32-bit windows apparently the first frame isn't our backtrace | |
41 | // frame but it's actually this frame. I'm not entirely sure why, but at | |
42 | // least it seems consistent? | |
43 | let o = if cfg!(all(windows, target_pointer_width = "32")) {1} else {0}; | |
44 | // frame offset 0 is the `backtrace::trace` function, but that's generic | |
45 | assert_frame(&v, o, 1, frame_4 as usize, "frame_4", | |
46 | "tests/smoke.rs", start_line + 6); | |
47 | assert_frame(&v, o, 2, frame_3 as usize, "frame_3", "tests/smoke.rs", | |
48 | start_line + 3); | |
49 | assert_frame(&v, o, 3, frame_2 as usize, "frame_2", "tests/smoke.rs", | |
50 | start_line + 2); | |
51 | assert_frame(&v, o, 4, frame_1 as usize, "frame_1", "tests/smoke.rs", | |
52 | start_line + 1); | |
53 | assert_frame(&v, o, 5, smoke_test_frames as usize, | |
54 | "smoke_test_frames", "", 0); | |
55 | } | |
56 | ||
57 | fn assert_frame(syms: &[(*mut c_void, *mut c_void)], | |
58 | offset: usize, | |
59 | idx: usize, | |
60 | actual_fn_pointer: usize, | |
61 | expected_name: &str, | |
62 | expected_file: &str, | |
63 | expected_line: u32) { | |
64 | if offset > idx { return } | |
65 | let (ip, sym) = syms[idx - offset]; | |
66 | let ip = ip as usize; | |
67 | let sym = sym as usize; | |
68 | assert!(ip >= sym); | |
69 | assert!(sym >= actual_fn_pointer); | |
70 | ||
71 | // windows dbghelp is *quite* liberal (and wrong) in many of its reports | |
72 | // right now... | |
73 | if !DBGHELP { | |
74 | assert!(sym - actual_fn_pointer < 1024); | |
75 | } | |
76 | ||
77 | let mut resolved = 0; | |
ea8adc8c | 78 | let can_resolve = DLADDR || LIBBACKTRACE || CORESYMBOLICATION || DBGHELP || GIMLI_SYMBOLIZE; |
7cac9316 XL |
79 | |
80 | let mut name = None; | |
81 | let mut addr = None; | |
82 | let mut line = None; | |
83 | let mut file = None; | |
84 | backtrace::resolve(ip as *mut c_void, |sym| { | |
85 | resolved += 1; | |
86 | name = sym.name().map(|v| v.to_string()); | |
87 | addr = sym.addr(); | |
88 | line = sym.lineno(); | |
89 | file = sym.filename().map(|v| v.to_path_buf()); | |
90 | }); | |
91 | ||
92 | // dbghelp doesn't always resolve symbols right now | |
93 | match resolved { | |
94 | 0 => return assert!(!can_resolve || DBGHELP), | |
95 | _ => {} | |
96 | } | |
97 | ||
98 | // * linux dladdr doesn't work (only consults local symbol table) | |
99 | // * windows dbghelp isn't great for GNU | |
100 | if can_resolve && | |
101 | !(cfg!(target_os = "linux") && DLADDR) && | |
102 | !(DBGHELP && !MSVC) | |
103 | { | |
104 | let name = name.expect("didn't find a name"); | |
105 | assert!(name.contains(expected_name), | |
106 | "didn't find `{}` in `{}`", expected_name, name); | |
107 | } | |
108 | ||
109 | if can_resolve { | |
110 | addr.expect("didn't find a symbol"); | |
111 | } | |
112 | ||
113 | if (LIBBACKTRACE || CORESYMBOLICATION || (DBGHELP && MSVC)) && cfg!(debug_assertions) { | |
114 | let line = line.expect("didn't find a line number"); | |
115 | let file = file.expect("didn't find a line number"); | |
116 | if !expected_file.is_empty() { | |
117 | assert!(file.ends_with(expected_file), | |
118 | "{:?} didn't end with {:?}", file, expected_file); | |
119 | } | |
120 | if expected_line != 0 { | |
121 | assert!(line == expected_line, | |
122 | "bad line number on frame for `{}`: {} != {}", | |
123 | expected_name, line, expected_line); | |
124 | } | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | #[test] | |
130 | fn many_threads() { | |
131 | let threads = (0..16).map(|_| { | |
132 | thread::spawn(|| { | |
133 | for _ in 0..16 { | |
134 | backtrace::trace(|frame| { | |
135 | backtrace::resolve(frame.ip(), |symbol| { | |
136 | let _s = symbol.name().map(|s| s.to_string()); | |
137 | }); | |
138 | true | |
139 | }); | |
140 | } | |
141 | }) | |
142 | }).collect::<Vec<_>>(); | |
143 | ||
144 | for t in threads { | |
145 | t.join().unwrap() | |
146 | } | |
147 | } | |
148 | ||
149 | #[test] | |
150 | #[cfg(feature = "rustc-serialize")] | |
151 | fn is_rustc_serialize() { | |
152 | extern crate rustc_serialize; | |
153 | ||
154 | fn is_encode<T: rustc_serialize::Encodable>() {} | |
155 | fn is_decode<T: rustc_serialize::Decodable>() {} | |
156 | ||
157 | is_encode::<backtrace::Backtrace>(); | |
158 | is_decode::<backtrace::Backtrace>(); | |
159 | } | |
160 | ||
161 | #[test] | |
162 | #[cfg(feature = "serde")] | |
163 | fn is_serde() { | |
164 | extern crate serde; | |
165 | ||
166 | fn is_serialize<T: serde::ser::Serialize>() {} | |
041b39d2 | 167 | fn is_deserialize<T: serde::de::DeserializeOwned>() {} |
7cac9316 XL |
168 | |
169 | is_serialize::<backtrace::Backtrace>(); | |
170 | is_deserialize::<backtrace::Backtrace>(); | |
171 | } |