Stepi now returning running state for handling program exit

This commit is contained in:
Elnath 2025-05-17 18:09:57 +02:00
parent b20fa4672d
commit 99999346ed
4 changed files with 29 additions and 8 deletions

7
Cargo.lock generated
View File

@ -37,6 +37,7 @@ name = "bdb"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"either",
"nix", "nix",
"thiserror", "thiserror",
] ]
@ -95,6 +96,12 @@ dependencies = [
"tracing-error", "tracing-error",
] ]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "eyre" name = "eyre"
version = "0.6.12" version = "0.6.12"

View File

@ -8,6 +8,7 @@ publish = false
nix = { version = "0.29.0", features = ["process", "ptrace"] } nix = { version = "0.29.0", features = ["process", "ptrace"] }
thiserror = "2.0.12" thiserror = "2.0.12"
color-eyre = "0.6.3" color-eyre = "0.6.3"
either = "1.15.0"
[build-dependencies] [build-dependencies]
color-eyre = "0.6.3" color-eyre = "0.6.3"

View File

@ -1,5 +1,5 @@
use either::{Either, Left, Right};
use nix::libc::user_regs_struct; use nix::libc::user_regs_struct;
use nix::sys::signal::Signal;
use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus}; use nix::sys::wait::{waitid, Id, WaitPidFlag, WaitStatus};
use nix::unistd::Pid; use nix::unistd::Pid;
@ -40,6 +40,7 @@ impl DebugState for Stopped {
fn pid(&self) -> Pid { self.pid } fn pid(&self) -> Pid { self.pid }
} }
#[allow(dead_code)]
impl DebugTarget<Stopped> { impl DebugTarget<Stopped> {
pub fn new(pid: Pid) -> Result<Self, WaitError> { pub fn new(pid: Pid) -> Result<Self, WaitError> {
waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).map_err(WaitError)?; waitid(Id::Pid(pid), WaitPidFlag::WSTOPPED).map_err(WaitError)?;
@ -51,12 +52,9 @@ impl DebugTarget<Stopped> {
Ok(DebugTarget { state: Running { pid: self.state.pid } }) Ok(DebugTarget { state: Running { pid: self.state.pid } })
} }
pub fn stepi(self) -> Result<Self, DebugError> { pub fn stepi(self) -> Result<DebugTarget<Running>, PTraceError> {
nix::sys::ptrace::step(self.state.pid, None).map_err(PTraceError)?; nix::sys::ptrace::step(self.state.pid, None).map_err(PTraceError)?;
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED).map_err(WaitError)? { Ok(DebugTarget { state: Running { pid: self.state.pid } })
WaitStatus::PtraceEvent(_pid, Signal::SIGTRAP, _c_event) => Ok(self),
status => Err(DebugError::UnexpectedWaitStatus(status)),
}
} }
pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> { pub fn get_registers(&self) -> Result<user_regs_struct, PTraceError> {
@ -72,8 +70,16 @@ impl DebugState for Running {
fn pid(&self) -> Pid { self.pid } fn pid(&self) -> Pid { self.pid }
} }
#[allow(dead_code)]
impl DebugTarget<Running> { impl DebugTarget<Running> {
pub fn wait_for_something(self) -> Result<Either<DebugTarget<Stopped>, i32>, DebugError> {
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WSTOPPED | WaitPidFlag::WEXITED).map_err(WaitError)? {
WaitStatus::Exited(_pid, exit_code) => Ok(Right(exit_code)),
WaitStatus::PtraceEvent(_pid, _signal, _c_event) => Ok(Left(DebugTarget { state: Stopped { pid: self.state.pid } })),
status => Err(DebugError::UnexpectedWaitStatus(status)),
}
}
pub fn wait_for_exit(self) -> Result<i32, DebugError> { pub fn wait_for_exit(self) -> Result<i32, DebugError> {
match waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED).map_err(WaitError)? { match waitid(Id::Pid(self.state.pid), WaitPidFlag::WEXITED).map_err(WaitError)? {
WaitStatus::Exited(_pid, exit_code) => Ok(exit_code), WaitStatus::Exited(_pid, exit_code) => Ok(exit_code),

View File

@ -3,6 +3,7 @@ mod debug_target;
use crate::debug_target::{DebugTarget, Stopped}; use crate::debug_target::{DebugTarget, Stopped};
use color_eyre::eyre::eyre; use color_eyre::eyre::eyre;
use either::Either;
use nix::libc::user_regs_struct; use nix::libc::user_regs_struct;
use nix::sys::ptrace::*; use nix::sys::ptrace::*;
use nix::sys::signal::Signal::*; use nix::sys::signal::Signal::*;
@ -17,7 +18,13 @@ fn single_step_all(mut target: DebugTarget<Stopped>) -> color_eyre::Result<()> {
instruction_number += 1; instruction_number += 1;
let regs = target.get_registers()?; let regs = target.get_registers()?;
println!("🔎 [==> {}] rip= {:#016x}, rax = 0x{rax:x} ({rax})", instruction_number, regs.rip, rax = regs.rax); println!("🔎 [==> {}] rip= {:#016x}, rax = 0x{rax:x} ({rax})", instruction_number, regs.rip, rax = regs.rax);
target = target.stepi()?; match target.stepi()?.wait_for_something()? {
Either::Left(t) => target = t,
Either::Right(exit_code) => {
println!("👋 Child exited with code {exit_code}");
return Ok(());
}
}
} }
} }