1extern crate num_derive;
8
9use foreign_types::{foreign_type, ForeignType};
10use libc::{c_char, c_void};
11use libunwind_sys::*;
12use num_derive::FromPrimitive;
13use num_traits::FromPrimitive;
14use std::ffi::CStr;
15use std::ffi::CString;
16use std::fmt;
17use std::mem::MaybeUninit;
18use std::path::Path;
19
20#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
23pub enum Error {
24 Succsess = 0,
26 Unspec = -1,
28 NoMem = -2,
30 BadReg = -3,
32 ReadOnlyReg = -4,
34 StopUnwind = -5,
36 InvalidIp = -6,
38 BadFrame = -7,
40 InVal = -8,
42 BadVersion = -9,
44 NoInfo = -10,
46}
47
48impl fmt::Display for Error {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 unsafe {
51 let e = CStr::from_ptr(unw_strerror(*self as i32));
52 write!(f, "{}", e.to_string_lossy())
53 }
54 }
55}
56
57pub struct Accessors(unw_accessors_t);
61
62impl Accessors {
63 #[cfg(feature = "ptrace")]
65 pub fn ptrace() -> &'static Accessors {
66 unsafe { &*(&_UPT_accessors as *const unw_accessors_t as *const Accessors) }
67 }
68 pub fn coredump() -> &'static Accessors {
70 unsafe { &*(&raw const _UCD_accessors as *const unw_accessors_t as *const Accessors) }
71 }
72}
73pub enum Byteorder {
75 Default = 0,
76 LitleEndian = 1234,
77 BigEndian = 3214,
78 PdpEndian = 3412,
79}
80
81foreign_type! {
82pub unsafe type AddressSpace {
84 type CType = libunwind_sys::unw_addr_space;
85 fn drop = unw_destroy_addr_space;
86 }
87}
88
89impl AddressSpace {
90 pub fn new(accessors: &Accessors, byteorder: Byteorder) -> Result<AddressSpace, Error> {
97 unsafe {
98 let ptr = unw_create_addr_space(
99 &accessors.0 as *const unw_accessors_t as *mut unw_accessors_t,
100 byteorder as i32,
101 );
102 if ptr.is_null() {
103 Err(Error::Unspec)
104 } else {
105 Ok(AddressSpace::from_ptr(ptr))
106 }
107 }
108 }
109}
110
111foreign_type! {
112 pub unsafe type CoredumpState {
114 type CType = libunwind_sys::UCD_info;
115 fn drop = _UCD_destroy;
116 }
117}
118
119impl CoredumpState {
120 pub fn new(core_path: &Path) -> Result<CoredumpState, Error> {
127 unsafe {
128 let core_path = CString::new(core_path.to_str().unwrap()).unwrap();
129 let ui = _UCD_create(core_path.as_ptr());
130 if ui.is_null() {
131 Err(Error::NoMem)
132 } else {
133 Ok(CoredumpState::from_ptr(ui))
134 }
135 }
136 }
137
138 pub fn pid(&mut self) -> i32 {
140 unsafe { _UCD_get_pid(self.0.as_ptr()) }
141 }
142 pub fn num_threads(&mut self) -> i32 {
144 unsafe { _UCD_get_num_threads(self.0.as_ptr()) }
145 }
146 pub fn select_thread(&mut self, id: i32) {
151 unsafe {
152 _UCD_select_thread(self.0.as_ptr(), id);
153 }
154 }
155
156 pub fn access_mem(&mut self, asp: &AddressSpace, address: usize) -> Result<usize, Error> {
163 unsafe {
164 let mut val: unw_word_t = 0;
165 let ret = _UCD_access_mem(
166 asp.0.as_ptr(),
167 address as unw_word_t,
168 &mut val,
169 0,
170 self.0.as_ptr() as *mut libc::c_void,
171 );
172 if ret == (Error::Succsess as i32) {
173 Ok(val as usize)
174 } else {
175 Err(FromPrimitive::from_i32(ret).unwrap())
176 }
177 }
178 }
179}
180
181#[cfg(feature = "ptrace")]
182foreign_type! {
183 pub unsafe type PtraceState {
185 type CType = libc::c_void;
186 fn drop = _UPT_destroy;
187 }
188}
189
190#[cfg(feature = "ptrace")]
191impl PtraceState {
192 pub fn new(pid: u32) -> Result<PtraceState, Error> {
197 unsafe {
198 let ptr = _UPT_create(pid as _);
199 if ptr.is_null() {
200 Err(Error::NoMem)
201 } else {
202 Ok(PtraceState::from_ptr(ptr))
203 }
204 }
205 }
206}
207
208#[derive(Clone, Copy)]
210pub struct ProcInfo {
211 start_ip: usize,
212 end_ip: usize,
213}
214
215impl ProcInfo {
216 pub fn start(&self) -> usize {
218 self.start_ip
219 }
220
221 pub fn end(&self) -> usize {
223 self.end_ip
224 }
225}
226
227#[derive(Clone)]
228pub struct Cursor(unw_cursor_t);
229impl Cursor {
230 pub fn coredump(
237 address_space: &mut AddressSpace,
238 state: &CoredumpState,
239 ) -> Result<Cursor, Error> {
240 unsafe {
241 let mut cursor = MaybeUninit::uninit();
242 let ret = unw_init_remote(
243 cursor.as_mut_ptr(),
244 address_space.0.as_ptr(),
245 state.0.as_ptr() as *mut c_void,
246 );
247 if ret == (Error::Succsess as i32) {
248 Ok(Cursor(cursor.assume_init()))
249 } else {
250 Err(FromPrimitive::from_i32(ret).unwrap())
251 }
252 }
253 }
254
255 #[cfg(feature = "ptrace")]
262 pub fn ptrace(address_space: &mut AddressSpace, state: &PtraceState) -> Result<Cursor, Error> {
263 unsafe {
264 let mut cursor = MaybeUninit::uninit();
265 let ret = unw_init_remote(
266 cursor.as_mut_ptr(),
267 address_space.0.as_ptr(),
268 state.0.as_ptr() as *mut c_void,
269 );
270 if ret == (Error::Succsess as i32) {
271 Ok(Cursor(cursor.assume_init()))
272 } else {
273 Err(FromPrimitive::from_i32(ret).unwrap())
274 }
275 }
276 }
277
278 pub fn local<F, T>(f: F) -> Result<T, Error>
283 where
284 F: FnOnce(Cursor) -> Result<T, Error>,
285 {
286 unsafe {
287 let mut context = MaybeUninit::uninit();
288 let ret = unw_getcontext(context.as_mut_ptr());
289 if ret != (Error::Succsess as i32) {
290 return Err(FromPrimitive::from_i32(ret).unwrap());
291 }
292 let mut context = context.assume_init();
293
294 let mut cursor = MaybeUninit::uninit();
295 let ret = unw_init_local(cursor.as_mut_ptr(), &mut context);
296 if ret != (Error::Succsess as i32) {
297 return Err(FromPrimitive::from_i32(ret).unwrap());
298 }
299
300 f(Cursor(cursor.assume_init()))
301 }
302 }
303
304 pub fn step(&mut self) -> Result<bool, Error> {
313 unsafe {
314 let ret = unw_step(&mut self.0);
315 if ret > 0 {
316 Ok(true)
317 } else if ret == 0 {
318 Ok(false)
319 } else {
320 Err(FromPrimitive::from_i32(ret).unwrap())
321 }
322 }
323 }
324
325 pub fn register(&mut self, id: i32) -> Result<usize, Error> {
330 unsafe {
331 let mut value = 0;
332 let ret = unw_get_reg(&self.0 as *const _ as *mut _, id, &mut value);
333 if ret == (Error::Succsess as i32) {
334 Ok(value as usize)
335 } else {
336 Err(FromPrimitive::from_i32(ret).unwrap())
337 }
338 }
339 }
340
341 pub fn ip(&mut self) -> Result<usize, Error> {
343 unsafe {
344 let mut value = 0;
345 let ret = unw_get_reg(
346 &self.0 as *const _ as *mut _,
347 libunwind_sys::UNW_TDEP_IP as i32,
348 &mut value,
349 );
350 if ret == (Error::Succsess as i32) {
351 Ok(value as usize)
352 } else {
353 Err(FromPrimitive::from_i32(ret).unwrap())
354 }
355 }
356 }
357
358 pub fn sp(&mut self) -> Result<usize, Error> {
360 unsafe {
361 let mut value = 0;
362 let ret = unw_get_reg(
363 &self.0 as *const _ as *mut _,
364 libunwind_sys::UNW_TDEP_SP as i32,
365 &mut value,
366 );
367 if ret == (Error::Succsess as i32) {
368 Ok(value as usize)
369 } else {
370 Err(FromPrimitive::from_i32(ret).unwrap())
371 }
372 }
373 }
374
375 pub fn proc_info(&mut self) -> Result<ProcInfo, Error> {
377 unsafe {
378 let mut info = MaybeUninit::uninit();
379 let ret = unw_get_proc_info(&self.0 as *const _ as *mut _, info.as_mut_ptr());
380 if ret == (Error::Succsess as i32) {
381 let info = info.assume_init();
382 Ok(ProcInfo {
383 start_ip: info.start_ip as usize,
384 end_ip: info.end_ip as usize,
385 })
386 } else {
387 Err(FromPrimitive::from_i32(ret).unwrap())
388 }
389 }
390 }
391
392 pub fn proc_name(&mut self) -> Result<String, Error> {
394 unsafe {
395 let mut name_vec = vec![0; 256];
396 let mut offset = 0;
397 let ret = unw_get_proc_name(
398 &self.0 as *const _ as *mut _,
399 name_vec.as_mut_ptr() as *mut c_char,
400 name_vec.len(),
401 &mut offset,
402 );
403 if ret == (Error::Succsess as i32) {
404 let name = CStr::from_ptr(name_vec.as_mut_ptr());
405 Ok(name.to_str().unwrap().to_string())
406 } else {
407 Err(FromPrimitive::from_i32(ret).unwrap())
408 }
409 }
410 }
411
412 pub fn is_signal_frame(&mut self) -> Result<bool, Error> {
414 unsafe {
415 let ret = unw_is_signal_frame(&self.0 as *const _ as *mut _);
416 if ret < 0 {
417 Err(FromPrimitive::from_i32(ret).unwrap())
418 } else {
419 Ok(ret != 0)
420 }
421 }
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use crate::*;
428 use std::path::PathBuf;
429
430 fn unwind_core_dump(core_filename: &str) -> String {
432 let mut core_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
433 core_path_buf.push(core_filename);
434
435 let mut state = CoredumpState::new(&core_path_buf).unwrap();
436 let mut address_space =
437 AddressSpace::new(Accessors::coredump(), Byteorder::Default).unwrap();
438 let mut cursor = Cursor::coredump(&mut address_space, &state).unwrap();
439
440 let mut backtrace = String::new();
441 loop {
442 let ip = cursor.ip().unwrap();
443 let sp = cursor.sp().unwrap();
444 if let Err(_e) = state.access_mem(&address_space, sp) {
445 assert!(false);
446 }
447
448 let name = cursor.proc_name().unwrap_or_else(|_| "<unknown>".to_string());
449 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name));
450 match cursor.step() {
451 Ok(ret) => {
452 if ret == false {
453 break;
454 }
455 }
456 Err(_) => {
457 break;
458 }
459 }
460 }
461
462 backtrace
463 }
464
465 #[test]
466 #[cfg(target_arch = "x86_64")]
467 fn test_core_unwind() {
468 let backtrace = unwind_core_dump("data/core.test_callstack");
469 assert!(backtrace.contains("0x40054b"), "{}", true);
470 assert!(backtrace.contains("0x400527"), "{}", true);
471 assert!(backtrace.contains("0x4004fd"), "{}", true);
472 assert!(backtrace.contains("0x400579"), "{}", true);
473 }
474
475 #[test]
476 #[cfg(target_arch = "x86_64")]
477 fn test_core_unwind_heap_error() {
478 let backtrace = unwind_core_dump("data/core.test_heapError");
479 assert!(backtrace.contains("0x7f90e05c0428"), "{}", true);
480 assert!(backtrace.contains("0x7f90e060b37a"), "{}", true);
481 }
482
483 #[test]
484 #[cfg(target_arch = "x86_64")]
485 fn test_core_unwind_canary() {
486 let backtrace = unwind_core_dump("data/core.test_canary");
487 assert!(backtrace.contains("0x7fc14b36b428"), "{}", true);
488 assert!(backtrace.contains("0x7fc14b44f15c"), "{}", true);
489 }
490
491 #[test]
492 #[cfg(all(target_arch = "x86_64", feature = "ptrace"))]
493 fn test_remote_unwind() {
494 use libc::c_void;
495 use std::io;
496 use std::process::Command;
497 use std::ptr;
498 use std::thread;
499 use std::time::Duration;
500
501 let mut test_callstack_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
502 test_callstack_path_buf.push("data/test_callstack_remote");
503 let mut child = Command::new(test_callstack_path_buf.to_str().unwrap())
504 .spawn()
505 .expect("failed to execute child");
506 thread::sleep(Duration::from_millis(10));
507 unsafe {
508 let ret = libc::ptrace(
509 libc::PTRACE_ATTACH,
510 child.id() as libc::pid_t,
511 ptr::null_mut::<c_void>(),
512 ptr::null_mut::<c_void>(),
513 );
514 if ret != 0 {
515 panic!("{}", io::Error::last_os_error());
516 }
517 loop {
518 let mut status = 0;
519 let ret = libc::waitpid(child.id() as libc::pid_t, &mut status, 0);
520 if ret < 0 {
521 panic!("{}", io::Error::last_os_error());
522 }
523 if libc::WIFSTOPPED(status) {
524 break;
525 }
526 }
527 }
528 let state = PtraceState::new(child.id()).unwrap();
529 let mut address_space = AddressSpace::new(Accessors::ptrace(), Byteorder::Default).unwrap();
530 let mut cursor = Cursor::ptrace(&mut address_space, &state).unwrap();
531
532 let mut backtrace = String::new();
533 loop {
534 let ip = cursor.ip().unwrap();
535 let name = cursor.proc_name().unwrap();
536 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name));
537 let ret = cursor.step().unwrap();
538 if ret == false {
539 break;
540 }
541 }
542 assert!(backtrace.contains("main"), true);
543 assert!(backtrace.contains("first"), true);
544 assert!(backtrace.contains("second"), true);
545 assert!(backtrace.contains("third"), true);
546 child.kill().unwrap();
547 }
548
549 #[test]
550 #[cfg(target_arch = "x86_64")]
551 fn test_local_unwind() {
552 let backtrace = Cursor::local(|mut cursor| {
553 let mut backtrace = String::new();
554 loop {
555 let ip = cursor.ip().unwrap();
556 let name = cursor.proc_name().unwrap();
557 backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name));
558 let ret = cursor.step().unwrap();
559 if ret == false {
560 break;
561 }
562 }
563 Ok(backtrace)
564 })
565 .unwrap();
566
567 assert!(backtrace.contains("test_local_unwind"), "{}", true);
568 assert!(
569 backtrace.contains("start_thread") || backtrace.contains("start"),
570 "{}", true
571 );
572 }
573}