1#![allow(non_upper_case_globals)]
9#![allow(non_camel_case_types)]
10#![allow(non_snake_case)]
11
12include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
13
14pub use crate::native::*;
15
16#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
17#[cfg_attr(target_arch = "x86", path = "x86.rs")]
18#[cfg_attr(target_arch = "arm", path = "arm.rs")]
19mod native;
20
21#[cfg(test)]
22mod tests {
23 extern crate libc;
24
25 use crate::*;
26 use libc::c_char;
27 use std::ffi::{CStr, CString};
28 use std::mem::MaybeUninit;
29 use std::path::PathBuf;
30
31 unsafe fn unwind_core_dump(core_file_name: &str) -> String {
32 let mut core_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
33 core_path_buf.push(format!("data/{}", core_file_name));
34
35 let core_path = CString::new(core_path_buf.to_str().unwrap()).unwrap();
36
37 let asp = unw_create_addr_space(&raw mut _UCD_accessors, 0);
38 let ui: *mut UCD_info = _UCD_create(core_path.as_ptr());
39 let mut c = MaybeUninit::uninit();
40 let _ret = unw_init_remote(c.as_mut_ptr(), asp, ui as *mut libc::c_void);
41
42 let mut ip: unw_word_t = 0;
43 let mut sp: unw_word_t = 0;
44 let mut val: unw_word_t = 0;
45 let mut backtrace = String::new();
46
47 loop {
48 unw_get_reg(
49 c.as_mut_ptr(),
50 UNW_TDEP_IP as ::std::os::raw::c_int,
51 &mut ip,
52 );
53 unw_get_reg(
54 c.as_mut_ptr(),
55 UNW_TDEP_SP as ::std::os::raw::c_int,
56 &mut sp,
57 );
58 let ret = _UCD_access_mem(asp, sp, &mut val, 0, ui as *mut libc::c_void);
59 if ret < 0 {
60 assert!(false);
61 }
62 let mut off = MaybeUninit::uninit();
63 let mut name_vec: Vec<c_char> = vec![0; 64];
64 unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
65 let name = CStr::from_ptr(name_vec.as_mut_ptr());
66 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
67 let ret = unw_step(c.as_mut_ptr());
68 if ret <= 0 {
69 break;
70 }
71 }
72
73 backtrace
74 }
75
76 #[test]
77 #[cfg(target_arch = "x86_64")]
78 fn test_core_unwind() {
79 unsafe {
80 let backtrace = unwind_core_dump("core.test_callstack");
81 assert!(backtrace.contains("0x40054b"));
82 assert!(backtrace.contains("0x400527"));
83 assert!(backtrace.contains("0x4004fd"));
84 assert!(backtrace.contains("0x400579"));
85 }
86 }
87
88 #[test]
89 #[cfg(target_arch = "x86_64")]
90 fn test_core_unwind_heap_error() {
91 unsafe {
92 let backtrace = unwind_core_dump("core.test_heapError");
93 assert!(backtrace.contains("0x3031306566633262"));
94 assert!(backtrace.contains("0x7f90e060b37a"));
95 }
96 }
97
98 #[test]
99 #[cfg(target_arch = "x86_64")]
100 fn test_core_unwind_canary() {
101 unsafe {
102 let backtrace = unwind_core_dump("core.test_canary");
103 println!("{:?}", &backtrace);
104 assert!(backtrace.contains("0x7fc14b44f15c"));
105 assert!(backtrace.contains("0x7fc14b36b428"));
106 }
107 }
108
109 #[test]
110 #[cfg(target_arch = "x86_64")]
111 fn test_local_unwind() {
112 unsafe {
113 let mut c = MaybeUninit::uninit();
114 let mut uc = MaybeUninit::uninit();
115 let mut ip: unw_word_t = 0;
116 let _ret = unw_getcontext(uc.as_mut_ptr());
117 let _ret = unw_init_local(c.as_mut_ptr(), uc.as_mut_ptr());
118 let mut backtrace = String::new();
119 loop {
120 unw_get_reg(
121 c.as_mut_ptr(),
122 UNW_TDEP_IP as ::std::os::raw::c_int,
123 &mut ip,
124 );
125 let mut off = MaybeUninit::uninit();
126 let mut name_vec: Vec<c_char> = vec![0; 64];
127 unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
128 let name = CStr::from_ptr(name_vec.as_mut_ptr());
129 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
130 let ret = unw_step(c.as_mut_ptr());
131 if ret <= 0 {
132 break;
133 }
134 }
135 assert!(backtrace.contains("test_local_unwind"));
136 assert!(backtrace.contains("start_thread") || backtrace.contains("start"));
137 }
138 }
139
140 #[test]
141 #[cfg(all(feature = "ptrace", target_arch = "x86_64"))]
142 fn test_remote_unwind() {
143 unsafe {
144 let mut c = MaybeUninit::uninit();
145 let mut ip: unw_word_t = 0;
146 let asp = unw_create_addr_space(&mut _UPT_accessors, 0);
147 let mut test_callstack_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
149 test_callstack_path_buf.push("data/test_callstack_remote");
150 let mut child = Command::new(test_callstack_path_buf.to_str().unwrap())
151 .spawn()
152 .expect("failed to execute child");
153 thread::sleep(Duration::from_millis(10));
154 let ret = libc::ptrace(
155 libc::PTRACE_ATTACH,
156 child.id() as libc::pid_t,
157 ptr::null_mut::<c_void>(),
158 ptr::null_mut::<c_void>(),
159 );
160 if ret != 0 {
161 panic!("{}", io::Error::last_os_error());
162 }
163
164 loop {
165 let mut status = 0;
166 let ret = libc::waitpid(child.id() as libc::pid_t, &mut status, 0);
167 if ret < 0 {
168 panic!("{}", io::Error::last_os_error());
169 }
170 if libc::WIFSTOPPED(status) {
171 break;
172 }
173 }
174
175 let ui: *mut ::std::os::raw::c_void = _UPT_create(child.id() as i32);
176 let mut backtrace = String::new();
177
178 let _ret = unw_init_remote(c.as_mut_ptr(), asp, ui as *mut libc::c_void);
179 loop {
180 unw_get_reg(
181 c.as_mut_ptr(),
182 UNW_TDEP_IP as ::std::os::raw::c_int,
183 &mut ip,
184 );
185 let mut off = MaybeUninit::uninit();
186 let mut name_vec: Vec<c_char> = vec![0; 64];
187 unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
188 let name = CStr::from_ptr(name_vec.as_mut_ptr());
189 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
190 let ret = unw_step(c.as_mut_ptr());
191 if ret <= 0 {
192 break;
193 }
194 }
195 assert!(backtrace.contains("main"));
196 assert!(backtrace.contains("first"));
197 assert!(backtrace.contains("second"));
198 assert!(backtrace.contains("third"));
199 _UPT_destroy(ui);
200 unw_destroy_addr_space(asp);
201 child.kill().unwrap();
202 }
203 }
204}