| use crate::error::Error; | |
| use crate::fmt; | |
| use crate::io; | |
| use crate::sys::backtrace::BacktraceContext; | |
| use crate::sys_common::backtrace::Frame; | |
| use unwind as uw; | |
| struct Context<'a> { | |
| idx: usize, | |
| frames: &'a mut [Frame], | |
| } | |
| #[derive(Debug)] | |
| struct UnwindError(uw::_Unwind_Reason_Code); | |
| impl Error for UnwindError { | |
| fn description(&self) -> &'static str { | |
| "unexpected return value while unwinding" | |
| } | |
| } | |
| impl fmt::Display for UnwindError { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "{}: {:?}", self.description(), self.0) | |
| } | |
| } | |
| #[inline(never)] // if we know this is a function call, we can skip it when | |
| // tracing | |
| pub fn unwind_backtrace(frames: &mut [Frame]) | |
| -> io::Result<(usize, BacktraceContext)> | |
| { | |
| let mut cx = Context { | |
| idx: 0, | |
| frames, | |
| }; | |
| let result_unwind = unsafe { | |
| uw::_Unwind_Backtrace(trace_fn, | |
| &mut cx as *mut Context<'_> | |
| as *mut libc::c_void) | |
| }; | |
| // See libunwind:src/unwind/Backtrace.c for the return values. | |
| // No, there is no doc. | |
| match result_unwind { | |
| // These return codes seem to be benign and need to be ignored for backtraces | |
| // to show up properly on all tested platforms. | |
| uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { | |
| Ok((cx.idx, BacktraceContext)) | |
| } | |
| _ => { | |
| Err(io::Error::new(io::ErrorKind::Other, | |
| UnwindError(result_unwind))) | |
| } | |
| } | |
| } | |
| extern fn trace_fn(ctx: *mut uw::_Unwind_Context, | |
| arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { | |
| let cx = unsafe { &mut *(arg as *mut Context<'_>) }; | |
| if cx.idx >= cx.frames.len() { | |
| return uw::_URC_NORMAL_STOP; | |
| } | |
| let mut ip_before_insn = 0; | |
| let mut ip = unsafe { | |
| uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void | |
| }; | |
| if !ip.is_null() && ip_before_insn == 0 { | |
| // this is a non-signaling frame, so `ip` refers to the address | |
| // after the calling instruction. account for that. | |
| ip = (ip as usize - 1) as *mut _; | |
| } | |
| // dladdr() on osx gets whiny when we use FindEnclosingFunction, and | |
| // it appears to work fine without it, so we only use | |
| // FindEnclosingFunction on non-osx platforms. In doing so, we get a | |
| // slightly more accurate stack trace in the process. | |
| // | |
| // This is often because panic involves the last instruction of a | |
| // function being "call std::rt::begin_unwind", with no ret | |
| // instructions after it. This means that the return instruction | |
| // pointer points *outside* of the calling function, and by | |
| // unwinding it we go back to the original function. | |
| let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { | |
| ip | |
| } else { | |
| unsafe { uw::_Unwind_FindEnclosingFunction(ip) } | |
| }; | |
| cx.frames[cx.idx] = Frame { | |
| symbol_addr: symaddr as *mut u8, | |
| exact_position: ip as *mut u8, | |
| inline_context: 0, | |
| }; | |
| cx.idx += 1; | |
| uw::_URC_NO_REASON | |
| } |