1#[cfg(target_os = "macos")]
55mod osx;
56#[cfg(target_os = "macos")]
57pub use osx::*;
58
59#[cfg(target_os = "linux")]
60mod linux;
61#[cfg(target_os = "linux")]
62pub use linux::*;
63
64#[cfg(target_os = "freebsd")]
65mod freebsd;
66#[cfg(target_os = "freebsd")]
67pub use freebsd::*;
68
69#[cfg(target_os = "windows")]
70mod windows;
71#[cfg(target_os = "windows")]
72pub use windows::*;
73
74#[derive(Debug)]
75pub enum Error {
76 NoBinaryForAddress(u64),
77 GoblinError(::goblin::error::Error),
78 IOError(std::io::Error),
79 Other(String),
80 #[cfg(use_libunwind)]
81 LibunwindError(linux::libunwind::Error),
82 #[cfg(target_os = "linux")]
83 NixError(nix::Error),
84}
85
86impl std::fmt::Display for Error {
87 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
88 match *self {
89 Error::NoBinaryForAddress(addr) => {
90 write!(
91 f,
92 "No binary found for address 0x{:016x}. Try reloading.",
93 addr
94 )
95 }
96 Error::GoblinError(ref e) => e.fmt(f),
97 Error::IOError(ref e) => e.fmt(f),
98 Error::Other(ref e) => write!(f, "{}", e),
99 #[cfg(use_libunwind)]
100 Error::LibunwindError(ref e) => e.fmt(f),
101 #[cfg(target_os = "linux")]
102 Error::NixError(ref e) => e.fmt(f),
103 }
104 }
105}
106
107impl std::error::Error for Error {
108 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
109 match *self {
110 Error::GoblinError(ref e) => Some(e),
111 Error::IOError(ref e) => Some(e),
112 #[cfg(use_libunwind)]
113 Error::LibunwindError(ref e) => Some(e),
114 #[cfg(target_os = "linux")]
115 Error::NixError(ref e) => Some(e),
116 _ => None,
117 }
118 }
119}
120
121impl From<goblin::error::Error> for Error {
122 fn from(err: goblin::error::Error) -> Error {
123 Error::GoblinError(err)
124 }
125}
126
127impl From<std::io::Error> for Error {
128 fn from(err: std::io::Error) -> Error {
129 Error::IOError(err)
130 }
131}
132
133#[cfg(target_os = "linux")]
134impl From<nix::Error> for Error {
135 fn from(err: nix::Error) -> Error {
136 Error::NixError(err)
137 }
138}
139
140#[cfg(use_libunwind)]
141impl From<linux::libunwind::Error> for Error {
142 fn from(err: linux::libunwind::Error) -> Error {
143 Error::LibunwindError(err)
144 }
145}
146
147#[derive(Debug, Clone)]
148pub struct StackFrame {
149 pub line: Option<u64>,
150 pub filename: Option<String>,
151 pub function: Option<String>,
152 pub module: String,
153 pub addr: u64,
154}
155
156impl std::fmt::Display for StackFrame {
157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
158 let function = self.function.as_ref().map(String::as_str).unwrap_or("?");
159 if let Some(filename) = self.filename.as_ref() {
160 write!(
161 f,
162 "0x{:016x} {} ({}:{})",
163 self.addr,
164 function,
165 filename,
166 self.line.unwrap_or(0)
167 )
168 } else {
169 write!(f, "0x{:016x} {} ({})", self.addr, function, self.module)
170 }
171 }
172}
173
174pub trait ProcessMemory {
175 fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error>;
178
179 fn copy(&self, addr: usize, length: usize) -> Result<Vec<u8>, Error> {
182 let mut data = vec![0; length];
183 self.read(addr, &mut data)?;
184 Ok(data)
185 }
186
187 fn copy_struct<T: Copy>(&self, addr: usize) -> Result<T, Error> {
189 let mut data = vec![0; std::mem::size_of::<T>()];
190 self.read(addr, &mut data)?;
191 Ok(unsafe { std::ptr::read(data.as_ptr() as *const _) })
192 }
193
194 fn copy_pointer<T: Copy>(&self, ptr: *const T) -> Result<T, Error> {
196 self.copy_struct(ptr as usize)
197 }
198
199 fn copy_vec<T: Copy>(&self, addr: usize, length: usize) -> Result<Vec<T>, Error> {
202 let mut vec = self.copy(addr, length * std::mem::size_of::<T>())?;
203 let capacity = vec.capacity() as usize / std::mem::size_of::<T>() as usize;
204 let ptr = vec.as_mut_ptr() as *mut T;
205 std::mem::forget(vec);
206 unsafe { Ok(Vec::from_raw_parts(ptr, capacity, capacity)) }
207 }
208}
209
210#[doc(hidden)]
211pub struct LocalProcess;
213impl ProcessMemory for LocalProcess {
214 fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error> {
215 unsafe {
216 std::ptr::copy_nonoverlapping(addr as *mut u8, buf.as_mut_ptr(), buf.len());
217 }
218 Ok(())
219 }
220}
221
222#[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))]
223#[doc(hidden)]
224fn filter_child_pids(
226 target_pid: Pid,
227 processes: &std::collections::HashMap<Pid, Pid>,
228) -> Vec<(Pid, Pid)> {
229 let mut ret = Vec::new();
230 for (child, parent) in processes.iter() {
231 let mut current = *parent;
232 loop {
233 if current == target_pid {
234 ret.push((*child, *parent));
235 break;
236 }
237 current = match processes.get(¤t) {
238 Some(pid) => {
239 if current == *pid {
240 break;
241 }
242 *pid
243 }
244 None => break,
245 };
246 }
247 }
248 ret
249}
250
251#[cfg(test)]
252pub mod tests {
253 use super::*;
254
255 #[derive(Copy, Clone)]
256 struct Point {
257 x: i32,
258 y: i64,
259 }
260
261 #[test]
262 fn test_copy_pointer() {
263 let original = Point { x: 15, y: 25 };
264 let copy = LocalProcess.copy_pointer(&original).unwrap();
265 assert_eq!(original.x, copy.x);
266 assert_eq!(original.y, copy.y);
267 }
268
269 #[test]
270 fn test_copy_struct() {
271 let original = Point { x: 10, y: 20 };
272 let copy: Point = LocalProcess
273 .copy_struct(&original as *const Point as usize)
274 .unwrap();
275 assert_eq!(original.x, copy.x);
276 assert_eq!(original.y, copy.y);
277 }
278}