### Get backtrace for a process using remoteprocess Source: https://docs.rs/remoteprocess/latest/index.html Example function that creates a process handle, gets an unwinder and symbolicator, iterates threads, locks each thread, and prints the callstack with symbolication. Requires the 'unwind' feature. Note: stack unwinding only works on x86_64 and OSX unwinding is unstable. ```rust #[cfg(feature="unwind")] fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> { // Create a new handle to the process let process = remoteprocess::Process::new(pid)?; // Create a stack unwind object, and use it to get the stack for each thread let unwinder = process.unwinder()?; let symbolicator = process.symbolicator()?; for thread in process.threads()?.iter() { println!("Thread {} - {}", thread.id()?, if thread.active()? { "running" } else { "idle" }); // lock the thread to get a consistent snapshot (unwinding will fail otherwise) // Note: the thread will appear idle when locked, so we are calling // thread.active() before this let _lock = thread.lock()?; // Iterate over the callstack for the current thread for ip in unwinder.cursor(&thread)? { let ip = ip?; // Lookup the current stack frame containing a filename/function/linenumber etc // for the current address symbolicator.symbolicate(ip, true, &mut |sf| { println!("\t{}", sf); })?; } } Ok(()) } ``` -------------------------------- ### Get backtrace for a process Source: https://docs.rs/remoteprocess Demonstrates how to attach to a process, iterate through its threads, and perform stack unwinding and symbolication. Requires the 'unwind' feature to be enabled. ```rust #[cfg(feature="unwind")] fn get_backtrace(pid: remoteprocess::Pid) -> Result<(), remoteprocess::Error> { // Create a new handle to the process let process = remoteprocess::Process::new(pid)?; // Create a stack unwind object, and use it to get the stack for each thread let unwinder = process.unwinder()?; let symbolicator = process.symbolicator()?; for thread in process.threads()?.iter() { println!("Thread {} - {}", thread.id()?, if thread.active()? { "running" } else { "idle" }); // lock the thread to get a consistent snapshot (unwinding will fail otherwise) // Note: the thread will appear idle when locked, so we are calling // thread.active() before this let _lock = thread.lock()?; // Iterate over the callstack for the current thread for ip in unwinder.cursor(&thread)? { let ip = ip?; // Lookup the current stack frame containing a filename/function/linenumber etc // for the current address symbolicator.symbolicate(ip, true, &mut |sf| { println!(" {}", sf); })?; } } Ok(()) } ``` -------------------------------- ### Get Process Tree Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Traverses the /proc directory to build a map of process IDs to their parent process IDs. ```rust fn get_process_tree() -> Result, Error> { let mut ret = HashMap::new(); for entry in std::fs::read_dir("/proc")? { let entry = entry?; let filename = entry.file_name(); let pid = match filename.to_str() { Some(pid) => pid, None => continue, }; if let Ok(pid) = pid.parse::() { match get_parent_pid(pid) { Ok(ppid) => ret.insert(pid, ppid), Err(_) => continue, }; } } Ok(ret) } ``` -------------------------------- ### Thread::lock() Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Locks a thread to get a consistent snapshot for unwinding. The thread will appear idle while locked. ```APIDOC ## Thread::lock() ### Description Locks a thread to get a consistent snapshot for unwinding. The thread will appear idle while locked. ### Method Method ### Parameters None ### Returns - **Result** - A lock guard on success, or an Error on failure. ### Example ```rust let _lock = thread.lock()?; ``` ``` -------------------------------- ### fn copy(&self, addr: usize, length: usize) -> Result, Error> Source: https://docs.rs/remoteprocess/latest/remoteprocess/trait.ProcessMemory.html Copies a series of bytes from another process. Main difference with 'read' is that this will allocate memory for you. ```APIDOC ## fn copy(&self, addr: usize, length: usize) -> Result, Error> ### Description Copies a series of bytes from another process. Main difference with 'read' is that this will allocate memory for you. ### Method copy ### Parameters - **addr** (usize) - Required - The address in the remote process to read from. - **length** (usize) - Required - The number of bytes to copy. ### Returns - **Result, Error>** - A vector of bytes on success, or an Error on failure. ``` -------------------------------- ### Namespace::new Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Namespace.html Creates a new Namespace for the given process ID. Returns a Result with the Namespace or an Error. ```APIDOC ## Namespace::new ### Description Creates a new Namespace for the given process ID. ### Method `pub fn new(pid: Pid) -> Result` ### Parameters - `pid` (Pid) - Required - The process ID for which to create the namespace. ### Returns - `Result` - The created Namespace or an error. ``` -------------------------------- ### Namespace Management Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Handles switching process namespaces and restoring them upon dropping the Namespace object. ```rust pub struct Namespace { ns_file: Option, } impl Namespace { pub fn new(pid: Pid) -> Result { let target_ns_filename = format!("/proc/{}/ns/mnt", pid); let self_mnt = std::fs::read_link("/proc/self/ns/mnt")?; let target_mnt = std::fs::read_link(&target_ns_filename)?; if self_mnt != target_mnt { info!("Process {} appears to be running in a different namespace - setting namespace to match", pid); let target = File::open(target_ns_filename)?; // need to open this here, gets trickier after changing the namespace let self_ns = File::open("/proc/self/ns/mnt")?; setns(target.as_fd(), CloneFlags::from_bits_truncate(0))?; Ok(Namespace { ns_file: Some(self_ns), }) } else { info!("Target process is running in same namespace - not changing"); Ok(Namespace { ns_file: None }) } } pub fn is_set(self) -> bool { self.ns_file.is_some() } } impl Drop for Namespace { fn drop(&mut self) { if let Some(ns_file) = self.ns_file.as_ref() { setns(ns_file.as_fd(), CloneFlags::from_bits_truncate(0)).unwrap(); info!("Restored process namespace"); } } } ``` -------------------------------- ### Process::threads() Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Lists all threads in the target process. ```APIDOC ## Process::threads() ### Description Lists all threads in the target process. ### Method Method ### Parameters None ### Returns - **Result** - A list of threads on success, or an Error on failure. ### Example ```rust for thread in process.threads()?.iter() { // ... } ``` ``` -------------------------------- ### Process::new(pid) Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Creates a new handle to a process by its PID. This is the primary entry point for interacting with a target process. ```APIDOC ## Process::new(pid) ### Description Creates a new handle to a process by its PID. This is the primary entry point for interacting with a target process. ### Method Constructor ### Parameters - **pid** (remoteprocess::Pid) - Required - The process ID of the target process. ### Returns - **Result** - A new Process handle on success, or an Error on failure. ### Example ```rust let process = remoteprocess::Process::new(pid)?; ``` ``` -------------------------------- ### LocalProcess mock implementation Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Provides a mock implementation of ProcessMemory for the local process, using unsafe pointer copying to read memory. Marked as doc(hidden). ```rust #[doc(hidden)] /// Mock for using ProcessMemory on the local process. 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(()) } } ``` -------------------------------- ### Unwinder and Symbolicator Configuration Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Conditional methods for initializing unwinder and symbolicator instances when libunwind is enabled. ```rust #[cfg(use_libunwind)] pub fn unwinder(&self) -> Result { Ok(Unwinder::new()?) } #[cfg(use_libunwind)] pub fn symbolicator(&self) -> Result { Ok(Symbolicator::new(self.pid)?) } ``` -------------------------------- ### fn copy_struct(&self, addr: usize) -> Result Source: https://docs.rs/remoteprocess/latest/remoteprocess/trait.ProcessMemory.html Copies a structure from another process. ```APIDOC ## fn copy_struct(&self, addr: usize) -> Result ### Description Copies a structure from another process. ### Method copy_struct ### Parameters - **addr** (usize) - Required - The address in the remote process where the structure is located. ### Returns - **Result** - The copied structure on success, or an Error on failure. ``` -------------------------------- ### StackFrame struct and Display implementation Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Defines the StackFrame struct with line, filename, function, module, and address fields. The Display implementation formats the frame as '0x{addr} {function} ({filename}:{line})' or '0x{addr} {function} ({module})' when filename is absent. ```rust #[derive(Debug, Clone)] pub struct StackFrame { pub line: Option, pub filename: Option, pub function: Option, 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) } } } ``` -------------------------------- ### Thread::new Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Thread.html Creates a new Thread instance from a thread ID. ```APIDOC ## Thread::new ### Description Creates a new Thread instance from a thread ID. ### Method new ### Signature pub fn new(threadid: i32) -> Result ### Parameters - **threadid** (i32) - Required - The thread ID to create the Thread for. ### Returns - **Result** - A Result containing the new Thread on success, or an Error on failure. ``` -------------------------------- ### Process::symbolicator() Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Creates a symbolicator object for the process, used to resolve symbols for addresses in the target process. ```APIDOC ## Process::symbolicator() ### Description Creates a symbolicator object for the process, used to resolve symbols for addresses in the target process. ### Method Method ### Parameters None ### Returns - **Result** - A symbolicator on success, or an Error on failure. ### Example ```rust let symbolicator = process.symbolicator()?; ``` ``` -------------------------------- ### Thread Management Methods Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Methods for creating, locking, and checking the status of a thread. ```rust impl Thread { pub fn new(threadid: i32) -> Result { Ok(Thread { tid: nix::unistd::Pid::from_raw(threadid), }) } pub fn lock(&self) -> Result { Ok(ThreadLock::new(self.tid)?) } pub fn id(&self) -> Result { Ok(self.tid.as_raw()) } /// True if this thread still exists and has not yet exited. fn exists(&self) -> bool { std::path::Path::new(&format!("/proc/{}/stat", self.tid)).exists() } pub fn active(&self) -> Result { let mut file = File::open(format!("/proc/{}/stat", self.tid))?; let mut buf = [0u8; 512]; file.read(&mut buf)?; match get_active_status(&buf) { Some(stat) => Ok(stat == b'R'), None => Err(Error::Other(format!( "Failed to parse /proc/{}/stat", self.tid ))), } } } ``` -------------------------------- ### fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error> Source: https://docs.rs/remoteprocess/latest/remoteprocess/trait.ProcessMemory.html Copies memory from another process into an already allocated byte buffer. This is the required method of the trait. ```APIDOC ## fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error> ### Description Copies memory from another process into an already allocated byte buffer. ### Method read ### Parameters - **addr** (usize) - Required - The address in the remote process to read from. - **buf** (&mut [u8]) - Required - The buffer to write the read bytes into. ### Returns - **Result<(), Error>** - Ok(()) on success, or an Error on failure. ``` -------------------------------- ### Process Status Parsing Utilities Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Functions to extract active status and parent PID from /proc/[pid]/stat files using regex. ```rust fn get_active_status(stat: &[u8]) -> Option { // find the last ')' character, and return the active status field which // comes after it. The comm field itself can contain `)`, so we have to be // greedy, looking for the last `)` in the line. lazy_static! { static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(r"(?-u)^\d+ \(.+\) (\w)").unwrap(); } let caps = RE.captures(stat)?; Some(caps.get(1)?.as_bytes()[0]) } fn get_parent_pid(pid: Pid) -> Result { let mut file = File::open(format!("/proc/{}/stat", pid))?; let mut buf = [0u8; 512]; file.read(&mut buf)?; get_ppid_status(&buf).ok_or_else(|| Error::Other(format!("Failed to parse /proc/{}/stat", pid))) } fn get_ppid_status(stat: &[u8]) -> Option { lazy_static! { static ref RE: regex::bytes::Regex = regex::bytes::Regex::new(r"(?-u)^\d+ \(.+\) \w (\d+)").unwrap(); } let caps = RE.captures(stat)?; std::str::from_utf8(caps.get(1)?.as_bytes()) .ok()? .parse::() .ok() } ``` -------------------------------- ### Retrieve Child Processes Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Retrieves a list of child process IDs for the current process. ```rust pub fn child_processes(&self) -> Result, Error> { let processes = get_process_tree()?; Ok(crate::filter_child_pids(self.pid, &processes)) } ``` -------------------------------- ### Linux module source code for remoteprocess Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html This is the complete source code of the Linux module for the remoteprocess crate. It defines the Process and Thread structs and implements methods for retrieving process information (exe, cwd, cmdline), locking all threads, and enumerating threads. It uses nix for ptrace and setns, and read_process_memory for memory access. ```rust #[cfg(use_libunwind)] pub mod libunwind; #[cfg(use_libunwind)] mod symbolication; use lazy_static::lazy_static; use libc::pid_t; use log::{debug, info, warn}; use nix::{ self, sched::{setns, CloneFlags}, sys::ptrace, sys::wait, }; use std::collections::HashMap; use std::convert::TryInto; use std::fs::File; use std::io::Read; use std::os::fd::AsFd; use super::Error; #[cfg(use_libunwind)] pub use self::symbolication::*; #[cfg(use_libunwind)] pub use self::libunwind::Unwinder; use read_process_memory::{CopyAddress, ProcessHandle}; pub type Pid = pid_t; pub type Tid = pid_t; pub struct Process { pub pid: Pid, } #[derive(Eq, PartialEq, Hash, Copy, Clone)] pub struct Thread { tid: nix::unistd::Pid, } impl Process { pub fn new(pid: Pid) -> Result { Ok(Process { pid }) } pub fn exe(&self) -> Result { let path = std::fs::read_link(format!("/proc/{}/exe", self.pid))?; Ok(path.to_string_lossy().to_string()) } pub fn cwd(&self) -> Result { let path = std::fs::read_link(format!("/proc/{}/cwd", self.pid))?; Ok(path.to_string_lossy().to_string()) } pub fn cmdline(&self) -> Result, Error> { let mut f = std::fs::File::open(format!("/proc/{}/cmdline", self.pid))?; let mut buffer = Vec::new(); f.read_to_end(&mut buffer)?; let mut ret = Vec::new(); for arg in buffer.split(|b| *b == 0).filter(|b| b.len() > 0) { ret.push( String::from_utf8(arg.to_vec()) .map_err(|e| Error::Other(format!("Failed to convert utf8 {}", e)))?, ) } Ok(ret) } pub fn lock(&self) -> Result { let mut locks = Vec::new(); let mut locked = std::collections::HashSet::new(); let mut done = false; let mut all_locks_failed = true; // we need to lock each individual thread of the process, but // while we're doing this new threads could be created. keep // on creating new locks for each thread until no new locks are // created while !done { done = true; for thread in self.threads()? { let threadid = thread.id()?; if !locked.contains(&threadid) { match thread.lock() { Ok(lock) => { locks.push(lock); locked.insert(threadid); done = false; all_locks_failed = false; } Err(Error::NixError(nix::errno::Errno::ESRCH)) => { // the thread probably exited before we could get a lock continue; } Err(e @ Error::NixError(nix::errno::Errno::EPERM)) => { if !thread.exists() { // The thread was probably in the "exiting" state, which returns // EPERM to the caller. This thread is dead, we can not ptrace // it and we should just ignore it. // See https://elixir.bootlin.com/linux/v6.15.3/source/kernel/ptrace.c#L458 continue; } // We likely really have no permission, propagate the error return Err(e); } Err(e) => return Err(e), } } } } if all_locks_failed { return Err(Error::Other(format!("All threads failed to lock"))); } Ok(Lock { locks }) } pub fn threads(&self) -> Result, Error> { let mut ret = Vec::new(); let path = format!("/proc/{}/task", self.pid); let tasks = std::fs::read_dir(path)?; for entry in tasks { let entry = entry?; let filename = entry.file_name(); let thread = match filename.to_str() { Some(thread) => thread, None => continue, }; if let Ok(threadid) = thread.parse::() { ret.push(Thread { tid: nix::unistd::Pid::from_raw(threadid), }); } } Ok(ret) } } ``` -------------------------------- ### Process::unwinder() Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Creates a stack unwind object for the process, used to obtain stack traces for threads. ```APIDOC ## Process::unwinder() ### Description Creates a stack unwind object for the process, used to obtain stack traces for threads. ### Method Method ### Parameters None ### Returns - **Result** - A stack unwinder on success, or an Error on failure. ### Example ```rust let unwinder = process.unwinder()?; ``` ``` -------------------------------- ### Namespace::is_set Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Namespace.html Checks whether the namespace is set. Returns a boolean indicating if the namespace is set. ```APIDOC ## Namespace::is_set ### Description Checks whether the namespace is set. ### Method `pub fn is_set(self) -> bool` ### Parameters - `self` - Required - The Namespace instance. ### Returns - `bool` - `true` if the namespace is set, `false` otherwise. ``` -------------------------------- ### Process Parsing Tests Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Unit tests for verifying active status and parent PID parsing logic. ```rust #[test] fn test_parse_active_stat() { assert_eq!(get_active_status(b"1234 (bash) S 1233"), Some(b'S')); assert_eq!(get_active_status(b"1234 (with space) R 1233"), Some(b'R')); assert_eq!(get_active_status(b"1234"), None); assert_eq!(get_active_status(b")"), None); assert_eq!(get_active_status(b")))"), None); assert_eq!(get_active_status(b"1234 (bash)S"), None); assert_eq!(get_active_status(b"1234)SSSS"), None); assert_eq!( get_active_status(b"15379 (ipython) t 9898 15379 9898 34816"), Some(b't') ); // comm may itself contain `)`: assert_eq!( get_active_status(b"83 (Thread.()) S 1 19"), Some(b'S') ); // Invalid UTF-8 and whitespace: assert_eq!(get_active_status(b"83 (\xc3\x28)) S ) R 1 19"), Some(b'R')); } #[test] fn test_parse_ppid_stat() { assert_eq!(get_ppid_status(b"1234 (bash) S 1233"), Some(1233)); assert_eq!(get_ppid_status(b"1234 (with space) R 1233"), Some(1233)); assert_eq!(get_ppid_status(b"1234"), None); assert_eq!(get_ppid_status(b")"), None); assert_eq!(get_ppid_status(b")))"), None); assert_eq!(get_ppid_status(b"1234 (bash)S"), None); assert_eq!(get_ppid_status(b"1234)SSSS"), None); assert_eq!( get_ppid_status(b"15379 (ipython) t 9898 15379 9898 34816"), Some(9898) ); // comm may itself contain `)`: assert_eq!(get_ppid_status(b"83 (Thread.()) S 1 19"), Some(1)); // Invalid UTF-8 and whitespace: assert_eq!(get_ppid_status(b"83 (\xc3\x28)) S ) R 1 19"), Some(1)); } ``` -------------------------------- ### Symbolicator::symbolicate(ip, with_inline, callback) Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Resolves symbols for an address in the target process and invokes a callback with the stack frame information. ```APIDOC ## Symbolicator::symbolicate(ip, with_inline, callback) ### Description Resolves symbols for an address in the target process and invokes a callback with the stack frame information. ### Method Method ### Parameters - **ip** (u64) - Required - The instruction pointer address to symbolicate. - **with_inline** (bool) - Required - Whether to include inline frames. - **callback** (FnMut(&StackFrame)) - Required - A callback that receives each stack frame. ### Returns - **Result<(), Error>** - Ok on success, or an Error on failure. ### Example ```rust symbolicator.symbolicate(ip, true, &mut |sf| { println!("\t{}", sf); })?; ``` ``` -------------------------------- ### fn copy_pointer(&self, ptr: *const T) -> Result Source: https://docs.rs/remoteprocess/latest/remoteprocess/trait.ProcessMemory.html Given a pointer that points to a struct in another process, returns the struct. ```APIDOC ## fn copy_pointer(&self, ptr: *const T) -> Result ### Description Given a pointer that points to a struct in another process, returns the struct. ### Method copy_pointer ### Parameters - **ptr** (*const T) - Required - A pointer to the structure in the remote process. ### Returns - **Result** - The copied structure on success, or an Error on failure. ``` -------------------------------- ### fn copy_vec(&self, addr: usize, length: usize) -> Result, Error> Source: https://docs.rs/remoteprocess/latest/remoteprocess/trait.ProcessMemory.html Copies a series of bytes from another process into a vector of structures of type T. ```APIDOC ## fn copy_vec(&self, addr: usize, length: usize) -> Result, Error> ### Description Copies a series of bytes from another process into a vector of structures of type T. ### Method copy_vec ### Parameters - **addr** (usize) - Required - The address in the remote process to read from. - **length** (usize) - Required - The number of elements of type T to copy. ### Returns - **Result, Error>** - A vector of structures on success, or an Error on failure. ``` -------------------------------- ### ThreadLock Implementation Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Handles attaching to and pausing a thread using ptrace. ```rust impl ThreadLock { fn new(tid: nix::unistd::Pid) -> Result { // This attaches to the process w/o pausing it. ptrace::seize( tid, // Without this, it *appears* that the tracee can get stuck in the // zombie state and our `waitpid` below will just hang. ptrace::Options::PTRACE_O_TRACEEXIT, )?; // Pause the process using `interrupt`. Unlike `attach`, this doesn't // use `SIGSTOP` or cause execve to send a `SIGTRAP` and so avoids races // with signals from foreign processes. if let Err(e) = ptrace::interrupt(tid) { if let Err(e) = ptrace::detach(tid, None) { warn!("Failed to detach from thread {} for cleanup: {}", tid, e); } return Err(Error::NixError(e)); } // Verify that the thread has stopped. loop { match wait::waitpid( tid, Some(wait::WaitPidFlag::WSTOPPED | wait::WaitPidFlag::__WALL), )? { // We only really expect to see a `PTRACE_EVENT_STOP`. wait::WaitStatus::PtraceEvent( _, nix::sys::signal::Signal::SIGTRAP | nix::sys::signal::Signal::SIGTSTP, event, ) if event == ptrace::Event::PTRACE_EVENT_STOP as i32 || event == ptrace::Event::PTRACE_EVENT_EXIT as i32 => { break } // However, experimentally, it appears we see an exit status when // a process is dying. wait::WaitStatus::Exited(_, _) => break, // Just re-injecting other signals that aren't ours. wait::WaitStatus::Stopped(_, sig) => { info!("reinjecting non-SIGSTOP signal {} to {}", sig, tid); ptrace::cont(tid, sig)?; } // Report an error on everything else. status => { return Err(Error::Other(format!( ``` -------------------------------- ### StackFrame Struct Definition Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.StackFrame.html The StackFrame struct definition with its public fields. ```APIDOC ## Struct StackFrame ### Description Represents a stack frame in a remote process. ### Definition ```rust pub struct StackFrame { pub line: Option, pub filename: Option, pub function: Option, pub module: String, pub addr: u64, } ``` ### Fields - **line** (Option) - Optional line number. - **filename** (Option) - Optional filename. - **function** (Option) - Optional function name. - **module** (String) - Module name. - **addr** (u64) - Address. ### Trait Implementations - **Clone** - Provides clone and clone_from methods. - **Debug** - Provides fmt for formatting. - **Display** - Provides fmt for display. ``` -------------------------------- ### Unit tests for copy_pointer and copy_struct Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Contains unit tests for the ProcessMemory trait using the LocalProcess mock. Tests copy_pointer and copy_struct with a Point struct. ```rust #[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); } } ``` -------------------------------- ### filter_child_pids helper function Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Filters a process map to return only child processes that are descendants of the target PID. Only compiled on Linux, Windows, or FreeBSD. ```rust #[cfg(any(target_os = "linux", target_os = "windows", target_os = "freebsd"))] #[doc(hidden)] /// Filters pids to own include descendations of target_pid fn filter_child_pids( target_pid: Pid, processes: &std::collections::HashMap, ) -> 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 } ``` -------------------------------- ### Read Process Memory Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Implements memory reading for a process using a process handle. ```rust impl super::ProcessMemory for Process { fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error> { let handle: ProcessHandle = self.pid.try_into()?; Ok(handle.copy_address(addr, buf)?) } } ``` -------------------------------- ### Unwinder::cursor(&thread) Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Iterates over the call stack for a given thread. ```APIDOC ## Unwinder::cursor(&thread) ### Description Iterates over the call stack for a given thread. ### Method Method ### Parameters - **thread** (&Thread) - Required - The thread to unwind. ### Returns - **Result** - A cursor that yields instruction pointers on success, or an Error on failure. ### Example ```rust for ip in unwinder.cursor(&thread)? { let ip = ip?; // ... } ``` ``` -------------------------------- ### From for Error Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Converts a linux::libunwind::Error into the library's Error type, wrapping it in the LibunwindError variant. Only compiled when the use_libunwind feature is enabled. ```rust #[cfg(use_libunwind)] impl From for Error { fn from(err: linux::libunwind::Error) -> Error { Error::LibunwindError(err) } } ``` -------------------------------- ### Thread::active() Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Returns whether a thread is currently running or idle. ```APIDOC ## Thread::active() ### Description Returns whether a thread is currently running or idle. ### Method Method ### Parameters None ### Returns - **Result** - `true` if the thread is running, `false` if idle, or an Error on failure. ### Example ```rust if thread.active()? { /* running */ } else { /* idle */ } ``` ``` -------------------------------- ### Thread::lock Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Thread.html Locks the thread, returning a ThreadLock handle. ```APIDOC ## Thread::lock ### Description Locks the thread, returning a ThreadLock handle. ### Method lock ### Signature pub fn lock(&self) -> Result ### Parameters - **self** (&Thread) - Required - The Thread instance to lock. ### Returns - **Result** - A Result containing the ThreadLock on success, or an Error on failure. ``` -------------------------------- ### ProcessMemory trait with default methods Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/lib.rs.html Defines the ProcessMemory trait for reading memory from another process. Provides default implementations for copy, copy_struct, copy_pointer, and copy_vec, which allocate buffers and read from the process. ```rust pub trait ProcessMemory { /// Copies memory from another process into an already allocated /// byte buffer fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), Error>; /// Copies a series of bytes from another process. Main difference /// with 'read' is that this will allocate memory for you fn copy(&self, addr: usize, length: usize) -> Result, Error> { let mut data = vec![0; length]; self.read(addr, &mut data)?; Ok(data) } /// Copies a structure from another process fn copy_struct(&self, addr: usize) -> Result { let mut data = vec![0; std::mem::size_of::()]; self.read(addr, &mut data)?; Ok(unsafe { std::ptr::read(data.as_ptr() as *const _) }) } /// Given a pointer that points to a struct in another process, returns the struct fn copy_pointer(&self, ptr: *const T) -> Result { self.copy_struct(ptr as usize) } /// Copies a series of bytes from another process into a vector of /// structures of type T. fn copy_vec(&self, addr: usize, length: usize) -> Result, Error> { let mut vec = self.copy(addr, length * std::mem::size_of::())?; let capacity = vec.capacity() as usize / std::mem::size_of::() as usize; let ptr = vec.as_mut_ptr() as *mut T; std::mem::forget(vec); unsafe { Ok(Vec::from_raw_parts(ptr, capacity, capacity)) } } } ``` -------------------------------- ### Thread::active Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Thread.html Checks if the thread is active. ```APIDOC ## Thread::active ### Description Checks if the thread is active. ### Method active ### Signature pub fn active(&self) -> Result ### Parameters - **self** (&Thread) - Required - The Thread instance to check. ### Returns - **Result** - A Result containing a boolean indicating if the thread is active, or an Error on failure. ``` -------------------------------- ### ThreadLock Drop Implementation Source: https://docs.rs/remoteprocess/latest/src/remoteprocess/linux/mod.rs.html Automatically detaches from a thread when the ThreadLock goes out of scope. ```rust impl Drop for ThreadLock { fn drop(&mut self) { if let Err(e) = ptrace::detach(self.tid, None) { warn!("Failed to detach from thread {} : {}", self.tid, e); } debug!("detached from thread {}", self.tid); } } ``` -------------------------------- ### Error enum definition Source: https://docs.rs/remoteprocess/latest/remoteprocess/enum.Error.html The Error enum represents possible errors in the remoteprocess crate. It includes variants for missing binary at an address, goblin errors, I/O errors, other errors, and nix errors. ```rust pub enum Error { NoBinaryForAddress(u64), GoblinError(Error), IOError(Error), Other(String), NixError(Error), } ``` -------------------------------- ### Thread::id Source: https://docs.rs/remoteprocess/latest/remoteprocess/struct.Thread.html Retrieves the thread ID of the Thread. ```APIDOC ## Thread::id ### Description Retrieves the thread ID of the Thread. ### Method id ### Signature pub fn id(&self) -> Result ### Parameters - **self** (&Thread) - Required - The Thread instance to get the ID from. ### Returns - **Result** - A Result containing the thread ID (Tid) on success, or an Error on failure. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.