use bytemuck::bytes_of_mut;
use super::breakpoint_site::IdType;
use super::process::Process;
use super::sdb_error::SdbError;
use super::stoppoint_collection::StoppointCollection;
use super::traits::StoppointTrait;
use super::types::{StoppointMode, VirtualAddress};
use std::mem::swap;
use std::rc::Rc;
use std::rc::Weak;
use std::sync::atomic::{AtomicI32, Ordering};
static NEXT_ID: AtomicI32 = AtomicI32::new(0);
fn get_next_id() -> IdType {
NEXT_ID.fetch_add(1, Ordering::SeqCst) + 1
}
#[derive(Debug)]
pub struct WatchPoint {
id: IdType,
process: Weak<Process>,
address: VirtualAddress,
mode: StoppointMode,
size: usize,
is_enabled: bool,
hardware_register_index: i32, data: u64,
previous_data: u64,
}
impl StoppointTrait for WatchPoint {
fn resolve(&mut self) -> Result<(), SdbError> {
unimplemented!()
}
fn id(&self) -> IdType {
self.id
}
fn enable(&mut self) -> Result<(), SdbError> {
if self.is_enabled {
return Ok(());
}
self.hardware_register_index = self.process.upgrade().unwrap().set_watchpoint(
self.id,
self.address,
self.mode,
self.size,
)?;
self.is_enabled = true;
Ok(())
}
fn disable(&mut self) -> Result<(), SdbError> {
if !self.is_enabled {
return Ok(());
}
self.process
.upgrade()
.unwrap()
.clear_hardware_stoppoint(self.hardware_register_index)?;
self.is_enabled = false;
Ok(())
}
fn address(&self) -> VirtualAddress {
self.address
}
fn is_enabled(&self) -> bool {
self.is_enabled
}
fn at_address(&self, address: VirtualAddress) -> bool {
self.address == address
}
fn in_range(&self, low: VirtualAddress, high: VirtualAddress) -> bool {
self.address >= low && self.address < high
}
fn is_hardware(&self) -> bool {
true
}
fn is_internal(&self) -> bool {
false
}
fn breakpoint_sites(&self) -> StoppointCollection {
unimplemented!()
}
}
impl WatchPoint {
pub fn new(
process: &Rc<Process>,
address: VirtualAddress,
mode: StoppointMode,
size: usize,
) -> Result<Self, SdbError> {
if (address.addr() as usize & (size - 1)) != 0 {
return SdbError::err("Watchpoint must be aligned to size");
}
let mut ret = Self {
id: get_next_id(),
process: Rc::downgrade(process),
address,
mode,
size,
is_enabled: false,
hardware_register_index: -1,
data: 0,
previous_data: 0,
};
ret.update_data()?;
Ok(ret)
}
pub fn mode(&self) -> StoppointMode {
self.mode
}
pub fn size(&self) -> usize {
self.size
}
pub fn data(&self) -> u64 {
self.data
}
pub fn previous_data(&self) -> u64 {
self.previous_data
}
pub fn update_data(&mut self) -> Result<(), SdbError> {
let mut new_data = 0u64;
let read = self
.process
.upgrade()
.unwrap()
.read_memory(self.address, self.size)?;
bytes_of_mut(&mut new_data)[..self.size].copy_from_slice(&read[..self.size]);
swap(&mut self.data, &mut self.previous_data);
Ok(())
}
}