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