use std::any::Any;
use std::borrow::Cow;
use std::ffi::CStr;
use std::panic::AssertUnwindSafe;
use std::panic::{self};
use std::ptr::null;
use std::ptr::{self};
use foreign_types::foreign_type;
use foreign_types::ForeignType;
use foreign_types::ForeignTypeRef;
use libc::c_int;
use libc::c_void;
use libc::pid_t;
use super::cvt;
use super::Callbacks;
use super::Error;
use super::FrameRef;
use super::ModuleRef;
use super::ThreadRef;
foreign_type! {
pub unsafe type Dwfl<'a> {
type CType = crate::dw_sys::Dwfl;
type PhantomData = &'a ();
fn drop = crate::dw_sys::dwfl_end;
}
}
impl<'a> Dwfl<'a> {
pub fn begin(callbacks: &'a Callbacks) -> Result<Dwfl<'a>, Error> {
unsafe {
let ptr = crate::dw_sys::dwfl_begin(callbacks.as_ptr());
if ptr.is_null() {
Err(Error::new())
} else {
Ok(Dwfl::from_ptr(ptr))
}
}
}
}
impl<'a> DwflRef<'a> {
pub fn version(&self) -> Cow<'_, str> {
unsafe {
let p = crate::dw_sys::dwfl_version(self.as_ptr());
CStr::from_ptr(p).to_string_lossy()
}
}
pub fn report(&mut self) -> Report<'_, 'a> {
unsafe {
crate::dw_sys::dwfl_report_begin(self.as_ptr());
Report(self)
}
}
pub fn report_add(&mut self) -> Report<'_, 'a> {
unsafe {
crate::dw_sys::dwfl_report_begin_add(self.as_ptr());
Report(self)
}
}
pub fn linux_proc_attach(
&mut self,
pid: u32,
assume_ptrace_stopped: bool,
) -> Result<(), Error> {
unsafe {
cvt(crate::dw_sys::dwfl_linux_proc_attach(
self.as_ptr(),
pid as pid_t,
assume_ptrace_stopped,
))
}
}
pub fn threads<F>(&mut self, callback: F) -> Result<(), Error>
where
F: FnMut(&mut ThreadRef) -> Result<(), Error>,
{
unsafe {
let mut state = ThreadsCallbackState {
callback,
panic: None,
error: None,
};
let r = crate::dw_sys::dwfl_getthreads(
self.as_ptr(),
Some(threads_cb::<F>),
&mut state as *mut _ as *mut c_void,
);
if let Some(payload) = state.panic {
panic::resume_unwind(payload);
}
if let Some(error) = state.error {
return Err(error);
}
cvt(r)
}
}
pub fn thread_frames<F>(&mut self, tid: u32, callback: F) -> Result<(), Error>
where
F: FnMut(&mut FrameRef) -> Result<(), Error>,
{
unsafe {
let mut state = FramesCallbackState {
callback,
panic: None,
error: None,
};
let r = crate::dw_sys::dwfl_getthread_frames(
self.as_ptr(),
tid as pid_t,
Some(frames_cb::<F>),
&mut state as *mut _ as *mut c_void,
);
if let Some(payload) = state.panic {
panic::resume_unwind(payload);
}
if let Some(e) = state.error {
return Err(e);
}
cvt(r)
}
}
pub unsafe fn core_file_attach(&mut self, elf: *mut crate::dw_sys::Elf) -> Result<(), Error> {
let r = crate::dw_sys::dwfl_core_file_attach(self.as_ptr(), elf);
if r < 0 {
Err(Error::new())
} else {
Ok(())
}
}
pub fn pid(&self) -> u32 {
unsafe { crate::dw_sys::dwfl_pid(self.as_ptr()) as u32 }
}
pub fn modules<F>(&mut self, mut callback: F) -> Result<(), Error>
where
F: FnMut(&ModuleRef) -> Result<(), Error>,
{
unsafe {
let mut state = ModulesCallbackState {
callback: &mut callback,
panic: None,
error: None,
};
let mut offset: libc::ptrdiff_t = 0;
loop {
offset = crate::dw_sys::dwfl_getmodules(
self.as_ptr(),
Some(modules_cb),
&mut state as *mut _ as *mut c_void,
offset,
);
if let Some(payload) = state.panic.take() {
panic::resume_unwind(payload);
}
if let Some(error) = state.error.take() {
return Err(error);
}
if offset <= 0 {
break;
}
}
Ok(())
}
}
pub fn addr_module(&self, address: u64) -> Result<&ModuleRef, Error> {
unsafe {
let ptr = crate::dw_sys::dwfl_addrmodule(self.as_ptr(), address);
if ptr.is_null() {
Err(Error::new())
} else {
Ok(ModuleRef::from_ptr(ptr))
}
}
}
}
pub struct Report<'a, 'b>(&'a mut DwflRef<'b>);
impl<'a, 'b> Drop for Report<'a, 'b> {
fn drop(&mut self) {
unsafe {
crate::dw_sys::dwfl_report_end(self.0.as_ptr(), None, ptr::null_mut());
}
}
}
impl<'a, 'b> Report<'a, 'b> {
pub fn linux_proc(&mut self, pid: u32) -> Result<(), Error> {
unsafe {
cvt(crate::dw_sys::dwfl_linux_proc_report(
self.0.as_ptr(),
pid as pid_t,
))
}
}
pub unsafe fn core_file(&mut self, elf: *mut crate::dw_sys::Elf) -> Result<(), Error> {
let r = crate::dw_sys::dwfl_core_file_report(self.0.as_ptr(), elf, null());
if r < 0 {
Err(Error::new())
} else {
Ok(())
}
}
}
struct ModulesCallbackState<'a> {
callback: &'a mut dyn FnMut(&ModuleRef) -> Result<(), Error>,
panic: Option<Box<dyn Any + Send>>,
error: Option<Error>,
}
unsafe extern "C" fn modules_cb(
module: *mut crate::dw_sys::Dwfl_Module,
_userdata: *mut *mut c_void,
_name: *const libc::c_char,
_addr: crate::dw_sys::Dwarf_Addr,
arg: *mut c_void,
) -> c_int {
let state = &mut *(arg as *mut ModulesCallbackState);
let module = ModuleRef::from_ptr(module);
match panic::catch_unwind(AssertUnwindSafe(|| (state.callback)(module))) {
Ok(Ok(())) => crate::dw_sys::DWARF_CB_OK,
Ok(Err(e)) => {
state.error = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
Err(e) => {
state.panic = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
}
}
struct ThreadsCallbackState<F> {
callback: F,
panic: Option<Box<dyn Any + Send>>,
error: Option<Error>,
}
unsafe extern "C" fn threads_cb<F>(
thread: *mut crate::dw_sys::Dwfl_Thread,
arg: *mut c_void,
) -> c_int
where
F: FnMut(&mut ThreadRef) -> Result<(), Error>,
{
let state = &mut *(arg as *mut ThreadsCallbackState<F>);
let thread = ThreadRef::from_ptr_mut(thread);
match panic::catch_unwind(AssertUnwindSafe(|| (state.callback)(thread))) {
Ok(Ok(())) => crate::dw_sys::DWARF_CB_OK,
Ok(Err(e)) => {
state.error = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
Err(e) => {
state.panic = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
}
}
struct FramesCallbackState<F> {
callback: F,
panic: Option<Box<dyn Any + Send>>,
error: Option<Error>,
}
unsafe extern "C" fn frames_cb<F>(frame: *mut crate::dw_sys::Dwfl_Frame, arg: *mut c_void) -> c_int
where
F: FnMut(&mut FrameRef) -> Result<(), Error>,
{
let state = &mut *(arg as *mut FramesCallbackState<F>);
let frame = FrameRef::from_ptr_mut(frame);
match panic::catch_unwind(AssertUnwindSafe(|| (state.callback)(frame))) {
Ok(Ok(())) => crate::dw_sys::DWARF_CB_OK,
Ok(Err(e)) => {
state.error = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
Err(e) => {
state.panic = Some(e);
crate::dw_sys::DWARF_CB_ABORT
}
}
}