| use super::prelude::*; |
| use crate::protocol::commands::ext::Breakpoints; |
| |
| use crate::arch::{Arch, BreakpointKind}; |
| |
| enum CmdKind { |
| Add, |
| Remove, |
| } |
| |
| impl<T: Target, C: Connection> GdbStubImpl<T, C> { |
| #[inline(always)] |
| fn handle_breakpoint_common( |
| &mut self, |
| ops: crate::target::ext::breakpoints::BreakpointsOps<'_, T>, |
| cmd: crate::protocol::commands::breakpoint::BasicBreakpoint<'_>, |
| cmd_kind: CmdKind, |
| ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { |
| let addr = |
| <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr).ok_or(Error::TargetMismatch)?; |
| |
| macro_rules! bp_kind { |
| () => { |
| BeBytes::from_be_bytes(cmd.kind) |
| .and_then(<T::Arch as Arch>::BreakpointKind::from_usize) |
| .ok_or(Error::TargetMismatch)? |
| }; |
| } |
| |
| let supported = match cmd.type_ { |
| 0 if ops.support_sw_breakpoint().is_some() => { |
| let ops = ops.support_sw_breakpoint().unwrap(); |
| let bp_kind = bp_kind!(); |
| match cmd_kind { |
| CmdKind::Add => ops.add_sw_breakpoint(addr, bp_kind), |
| CmdKind::Remove => ops.remove_sw_breakpoint(addr, bp_kind), |
| } |
| } |
| 1 if ops.support_hw_breakpoint().is_some() => { |
| let ops = ops.support_hw_breakpoint().unwrap(); |
| let bp_kind = bp_kind!(); |
| match cmd_kind { |
| CmdKind::Add => ops.add_hw_breakpoint(addr, bp_kind), |
| CmdKind::Remove => ops.remove_hw_breakpoint(addr, bp_kind), |
| } |
| } |
| 2 | 3 | 4 if ops.support_hw_watchpoint().is_some() => { |
| use crate::target::ext::breakpoints::WatchKind; |
| let kind = match cmd.type_ { |
| 2 => WatchKind::Write, |
| 3 => WatchKind::Read, |
| 4 => WatchKind::ReadWrite, |
| #[allow(clippy::unreachable)] // will be optimized out |
| _ => unreachable!(), |
| }; |
| let len = <T::Arch as Arch>::Usize::from_be_bytes(cmd.kind) |
| .ok_or(Error::TargetMismatch)?; |
| let ops = ops.support_hw_watchpoint().unwrap(); |
| match cmd_kind { |
| CmdKind::Add => ops.add_hw_watchpoint(addr, len, kind), |
| CmdKind::Remove => ops.remove_hw_watchpoint(addr, len, kind), |
| } |
| } |
| // explicitly handle unguarded variants of known breakpoint types |
| 0 | 1 | 2 | 3 | 4 => return Ok(HandlerStatus::Handled), |
| // warn if the GDB client ever sends a type outside the known types |
| other => { |
| warn!("unknown breakpoint type: {}", other); |
| return Ok(HandlerStatus::Handled); |
| } |
| }; |
| |
| match supported.handle_error()? { |
| true => Ok(HandlerStatus::NeedsOk), |
| false => Err(Error::NonFatalError(22)), |
| } |
| } |
| |
| pub(crate) fn handle_breakpoints<'a>( |
| &mut self, |
| _res: &mut ResponseWriter<'_, C>, |
| target: &mut T, |
| command: Breakpoints<'a>, |
| ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { |
| let ops = match target.support_breakpoints() { |
| Some(ops) => ops, |
| None => return Ok(HandlerStatus::Handled), |
| }; |
| |
| crate::__dead_code_marker!("breakpoints", "impl"); |
| |
| let handler_status = match command { |
| Breakpoints::z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Remove)?, |
| Breakpoints::Z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Add)?, |
| // TODO: handle ZWithBytecode once agent expressions are implemented |
| _ => HandlerStatus::Handled, |
| }; |
| Ok(handler_status) |
| } |
| } |