#[cfg(target_os = "macos")]
mod osx;
#[cfg(target_os = "macos")]
pub use osx::*;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
pub use linux::*;
#[cfg(target_os = "freebsd")]
mod freebsd;
#[cfg(target_os = "freebsd")]
pub use freebsd::*;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(target_os = "windows")]
pub use windows::*;
#[derive(Debug)]
pub enum Error {
NoBinaryForAddress(u64),
GoblinError(::goblin::error::Error),
IOError(std::io::Error),
Other(String),
#[cfg(use_libunwind)]
LibunwindError(linux::libunwind::Error),
#[cfg(target_os = "linux")]
NixError(nix::Error),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Error::NoBinaryForAddress(addr) => {
write!(
f,
"No binary found for address 0x{:016x}. Try reloading.",
addr
)
}
Error::GoblinError(ref e) => e.fmt(f),
Error::IOError(ref e) => e.fmt(f),
Error::Other(ref e) => write!(f, "{}", e),
#[cfg(use_libunwind)]
Error::LibunwindError(ref e) => e.fmt(f),
#[cfg(target_os = "linux")]
Error::NixError(ref e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Error::GoblinError(ref e) => Some(e),
Error::IOError(ref e) => Some(e),
#[cfg(use_libunwind)]
Error::LibunwindError(ref e) => Some(e),
#[cfg(target_os = "linux")]
Error::NixError(ref e) => Some(e),
_ => None,
}
}
}
impl From<goblin::error::Error> for Error {
fn from(err: goblin::error::Error) -> Error {
Error::GoblinError(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::IOError(err)
}
}
#[cfg(target_os = "linux")]
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Error {
Error::NixError(err)
}
}
#[cfg(use_libunwind)]
impl From<linux::libunwind::Error> for Error {
fn from(err: linux::libunwind::Error) -> Error {
Error::LibunwindError(err)
}
}
#[derive(Debug, Clone)]
pub struct StackFrame {
pub line: Option<u64>,
pub filename: Option<String>,
pub function: Option<String>,
pub module: String,
pub addr: u64,
}
impl std::fmt::Display for StackFrame {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let function = self.function.as_ref().map(String::as_str).unwrap_or("?");
if let Some(filename) = self.filename.as_ref() {
write!(
f,
"0x{:016x} {} ({}:{})",
self.addr,
function,
filename,
self.line.unwrap_or(0)
)
} else {
write!(f, "0x{:016x} {} ({})", self.addr, function, self.module)
}
}
}
pub trait ProcessMemory {
fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error>;
fn copy(&self, addr: usize, length: usize) -> Result<Vec<u8>, Error> {
let mut data = vec![0; length];
self.read(addr, &mut data)?;
Ok(data)
}
fn copy_struct<T: Copy>(&self, addr: usize) -> Result<T, Error> {
let mut data = vec![0; std::mem::size_of::<T>()];
self.read(addr, &mut data)?;
Ok(unsafe { std::ptr::read(data.as_ptr() as *const _) })
}
fn copy_pointer<T: Copy>(&self, ptr: *const T) -> Result<T, Error> {
self.copy_struct(ptr as usize)
}
fn copy_vec<T: Copy>(&self, addr: usize, length: usize) -> Result<Vec<T>, Error> {
let mut vec = self.copy(addr, length * std::mem::size_of::<T>())?;
let capacity = vec.capacity() as usize / std::mem::size_of::<T>() as usize;
let ptr = vec.as_mut_ptr() as *mut T;
std::mem::forget(vec);
unsafe { Ok(Vec::from_raw_parts(ptr, capacity, capacity)) }
}
}
#[doc(hidden)]
pub struct LocalProcess;
impl ProcessMemory for LocalProcess {
fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error> {
unsafe {
std::ptr::copy_nonoverlapping(addr as *mut u8, buf.as_mut_ptr(), buf.len());
}
Ok(())
}
}
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
#[doc(hidden)]
fn filter_child_pids(
target_pid: Pid,
processes: &std::collections::HashMap<Pid, Pid>,
) -> Vec<(Pid, Pid)> {
let mut ret = Vec::new();
for (child, parent) in processes.iter() {
let mut current = *parent;
loop {
if current == target_pid {
ret.push((*child, *parent));
break;
}
current = match processes.get(¤t) {
Some(pid) => {
if current == *pid {
break;
}
*pid
}
None => break,
};
}
}
ret
}
#[cfg(test)]
pub mod tests {
use super::*;
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i64,
}
#[test]
fn test_copy_pointer() {
let original = Point { x: 15, y: 25 };
let copy = LocalProcess.copy_pointer(&original).unwrap();
assert_eq!(original.x, copy.x);
assert_eq!(original.y, copy.y);
}
#[test]
fn test_copy_struct() {
let original = Point { x: 10, y: 20 };
let copy: Point = LocalProcess
.copy_struct(&original as *const Point as usize)
.unwrap();
assert_eq!(original.x, copy.x);
assert_eq!(original.y, copy.y);
}
}