use nix::unistd::Pid;
use serde::Serialize;
use tracing::{error, trace};
use crate::errors::{DebuggerError, Result};
use crate::{mem_read_word, mem_write_word, Addr, Word};
pub const MASK_ALL: Word = Word::MAX;
pub const INT3_BYTE: u8 = 0xcc;
pub const INT3: Word = INT3_BYTE as Word;
pub const WORD_MASK: Word = 0x0000_0000_0000_00ff;
pub const WORD_MASK_INV: Word = MASK_ALL ^ WORD_MASK;
#[derive(Debug, Clone, Hash, Serialize)]
pub struct Breakpoint {
addr: Addr,
#[serde(serialize_with = "ser_pid")]
pid: Pid,
saved_data: Option<u8>,
}
impl Breakpoint {
#[must_use]
pub fn new(pid: Pid, addr: Addr) -> Self {
Self {
pid,
addr,
saved_data: None,
}
}
#[inline]
#[must_use]
pub fn is_enabled(&self) -> bool {
self.saved_data.is_some()
}
#[allow(clippy::missing_panics_doc)] pub fn enable(&mut self) -> Result<()> {
if self.is_enabled() {
return Err(DebuggerError::BreakpointIsAlreadyEnabled);
}
let data_word: Word = mem_read_word(self.pid, self.addr)?;
trace!("original word: {data_word:016x}");
self.saved_data = Some((data_word & WORD_MASK) as u8);
trace!("saved_byte: {:02x}", self.saved_data.as_ref().unwrap());
let data_word_modified: Word = (data_word & WORD_MASK_INV) | INT3;
trace!("modified word: {data_word_modified:016x}");
mem_write_word(self.pid, self.addr, data_word_modified)?;
Ok(())
}
#[allow(clippy::missing_panics_doc)] pub fn disable(&mut self) -> Result<()> {
if !self.is_enabled() {
return Err(DebuggerError::BreakpointIsAlreadyDisabled);
}
let data_word: Word = mem_read_word(self.pid, self.addr)?;
trace!("breakpo: {data_word:016x}");
let data_word_restored: Word =
(data_word & WORD_MASK_INV) | Word::from(self.saved_data.unwrap());
trace!("restore: {data_word_restored:016x}");
mem_write_word(self.pid, self.addr, data_word_restored)?;
self.saved_data = None;
Ok(())
}
#[must_use]
pub fn saved_data(&self) -> Option<u8> {
self.saved_data
}
}
impl Drop for Breakpoint {
fn drop(&mut self) {
if self.is_enabled() {
if let Err(e) = self.disable() {
if matches!(e, DebuggerError::Os(nix::errno::Errno::ESRCH)) {
return;
}
error!("{e}");
}
}
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn ser_pid<S: serde::Serializer>(pid: &Pid, s: S) -> std::result::Result<S::Ok, S::Error> {
s.serialize_i32(pid.as_raw())
}
#[cfg(test)]
mod test {
#[test]
fn test_minus_one_has_this_representaiton() {
assert_eq!(
&(-1i64).to_le_bytes(),
&[0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8,]
);
}
}