blob: 70ed15ab0adc9d24bfb23685d5e870a0e9bde03f [file] [log] [blame]
//! Stop reasons reported back to the GDB client.
use crate::arch::Arch;
use crate::common::Signal;
use crate::common::Tid;
use crate::target::ext::base::reverse_exec::ReplayLogPosition;
use crate::target::ext::breakpoints::WatchKind;
use crate::target::ext::catch_syscalls::CatchSyscallPosition;
use crate::target::Target;
/// Describes why a thread stopped.
///
/// Single threaded targets should set `Tid` to `()`, whereas multi threaded
/// targets should set `Tid` to [`Tid`]. To make things easier, it is
/// recommended to use the [`SingleThreadStopReason`] and
/// [`MultiThreadStopReason`] when possible.
///
///
///
/// Targets MUST only respond with stop reasons that correspond to IDETs that
/// target has implemented. Not doing so will result in a runtime error.
///
/// e.g: A target which has not implemented the [`HwBreakpoint`] IDET must not
/// return a `HwBreak` stop reason. While this is not enforced at compile time,
/// doing so will result in a runtime `UnsupportedStopReason` error.
///
/// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum BaseStopReason<Tid, U> {
/// Completed the single-step request.
DoneStep,
/// The process exited with the specified exit status.
Exited(u8),
/// The process terminated with the specified signal number.
Terminated(Signal),
/// The program received a signal.
Signal(Signal),
/// A specific thread received a signal.
SignalWithThread {
/// Tid of the associated thread
tid: Tid,
/// The signal
signal: Signal,
},
/// A thread hit a software breakpoint (e.g. due to a trap instruction).
///
/// Requires: [`SwBreakpoint`].
///
/// NOTE: This does not necessarily have to be a breakpoint configured by
/// the client/user of the current GDB session.
///
/// [`SwBreakpoint`]: crate::target::ext::breakpoints::SwBreakpoint
SwBreak(Tid),
/// A thread hit a hardware breakpoint.
///
/// Requires: [`HwBreakpoint`].
///
/// [`HwBreakpoint`]: crate::target::ext::breakpoints::HwBreakpoint
HwBreak(Tid),
/// A thread hit a watchpoint.
///
/// Requires: [`HwWatchpoint`].
///
/// [`HwWatchpoint`]: crate::target::ext::breakpoints::HwWatchpoint
Watch {
/// Tid of the associated thread
tid: Tid,
/// Kind of watchpoint that was hit
kind: WatchKind,
/// Address of watched memory
addr: U,
},
/// The program has reached the end of the logged replay events.
///
/// Requires: [`ReverseCont`] or [`ReverseStep`].
///
/// This is used for GDB's reverse execution. When playing back a recording,
/// you may hit the end of the buffer of recorded events, and as such no
/// further execution can be done. This stop reason tells GDB that this has
/// occurred.
///
/// [`ReverseCont`]: crate::target::ext::base::reverse_exec::ReverseCont
/// [`ReverseStep`]: crate::target::ext::base::reverse_exec::ReverseStep
ReplayLog {
/// (optional) Tid of the associated thread.
tid: Option<Tid>,
/// The point reached in a replay log (i.e: beginning vs. end).
pos: ReplayLogPosition,
},
/// The program has reached a syscall entry or return location.
///
/// Requires: [`CatchSyscalls`].
///
/// [`CatchSyscalls`]: crate::target::ext::catch_syscalls::CatchSyscalls
CatchSyscall {
/// (optional) Tid of the associated thread.
tid: Option<Tid>,
/// The syscall number.
number: U,
/// The location the event occurred at.
position: CatchSyscallPosition,
},
}
/// A stop reason for a single threaded target.
///
/// Threads are identified using the unit type `()` (as there is only a single
/// possible thread-id).
pub type SingleThreadStopReason<U> = BaseStopReason<(), U>;
/// A stop reason for a multi threaded target.
///
/// Threads are identified using a [`Tid`].
pub type MultiThreadStopReason<U> = BaseStopReason<Tid, U>;
impl<U> From<BaseStopReason<(), U>> for BaseStopReason<Tid, U> {
fn from(st_stop_reason: BaseStopReason<(), U>) -> BaseStopReason<Tid, U> {
match st_stop_reason {
BaseStopReason::DoneStep => BaseStopReason::DoneStep,
BaseStopReason::Exited(code) => BaseStopReason::Exited(code),
BaseStopReason::Terminated(sig) => BaseStopReason::Terminated(sig),
BaseStopReason::SignalWithThread { signal, .. } => BaseStopReason::SignalWithThread {
tid: crate::SINGLE_THREAD_TID,
signal,
},
BaseStopReason::SwBreak(_) => BaseStopReason::SwBreak(crate::SINGLE_THREAD_TID),
BaseStopReason::HwBreak(_) => BaseStopReason::HwBreak(crate::SINGLE_THREAD_TID),
BaseStopReason::Watch { kind, addr, .. } => BaseStopReason::Watch {
tid: crate::SINGLE_THREAD_TID,
kind,
addr,
},
BaseStopReason::Signal(sig) => BaseStopReason::Signal(sig),
BaseStopReason::ReplayLog { pos, .. } => BaseStopReason::ReplayLog { tid: None, pos },
BaseStopReason::CatchSyscall {
number, position, ..
} => BaseStopReason::CatchSyscall {
tid: None,
number,
position,
},
}
}
}
mod private {
pub trait Sealed {}
impl<U> Sealed for super::SingleThreadStopReason<U> {}
impl<U> Sealed for super::MultiThreadStopReason<U> {}
}
/// A marker trait implemented by [`SingleThreadStopReason`] and
/// [`MultiThreadStopReason`].
pub trait IntoStopReason<T: Target>:
private::Sealed + Into<MultiThreadStopReason<<<T as Target>::Arch as Arch>::Usize>>
{
}
impl<T: Target> IntoStopReason<T> for SingleThreadStopReason<<<T as Target>::Arch as Arch>::Usize> {}
impl<T: Target> IntoStopReason<T> for MultiThreadStopReason<<<T as Target>::Arch as Arch>::Usize> {}