aboutsummaryrefslogtreecommitdiff
path: root/src/target/ext/base/multithread.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/target/ext/base/multithread.rs')
-rw-r--r--src/target/ext/base/multithread.rs407
1 files changed, 181 insertions, 226 deletions
diff --git a/src/target/ext/base/multithread.rs b/src/target/ext/base/multithread.rs
index eba9d79..693030e 100644
--- a/src/target/ext/base/multithread.rs
+++ b/src/target/ext/base/multithread.rs
@@ -1,127 +1,12 @@
//! Base debugging operations for multi threaded targets.
use crate::arch::Arch;
-use crate::common::*;
-use crate::target::ext::breakpoints::WatchKind;
+use crate::common::Signal;
+use crate::common::Tid;
use crate::target::{Target, TargetResult};
-use super::{ReplayLogPosition, SingleRegisterAccessOps};
-
-// Convenient re-exports
-pub use super::{GdbInterrupt, ResumeAction};
-
-/// Base debugging operations for multi threaded targets.
-#[allow(clippy::type_complexity)]
-pub trait MultiThreadOps: Target {
- /// Resume execution on the target.
- ///
- /// Prior to calling `resume`, `gdbstub` will call `clear_resume_actions`,
- /// followed by zero or more calls to `set_resume_action`, specifying any
- /// thread-specific resume actions.
- ///
- /// The `default_action` parameter specifies the "fallback" resume action
- /// for any threads that did not have a specific resume action set via
- /// `set_resume_action`. The GDB client typically sets this to
- /// `ResumeAction::Continue`, though this is not guaranteed.
- ///
- /// The `check_gdb_interrupt` callback can be invoked to check if GDB sent
- /// an Interrupt packet (i.e: the user pressed Ctrl-C). It's recommended to
- /// invoke this callback every-so-often while the system is running (e.g:
- /// every X cycles/milliseconds). Periodically checking for incoming
- /// interrupt packets is _not_ required, but it is _recommended_.
- ///
- /// # Implementation requirements
- ///
- /// These requirements cannot be satisfied by `gdbstub` internally, and must
- /// be handled on a per-target basis.
- ///
- /// ### Adjusting PC after a breakpoint is hit
- ///
- /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason)
- /// notes the following:
- ///
- /// > On some architectures, such as x86, at the architecture level, when a
- /// > breakpoint instruction executes the program counter points at the
- /// > breakpoint address plus an offset. On such targets, the stub is
- /// > responsible for adjusting the PC to point back at the breakpoint
- /// > address.
- ///
- /// Omitting PC adjustment may result in unexpected execution flow and/or
- /// breakpoints not working correctly.
- ///
- /// # Additional Considerations
- ///
- /// ### Bare-Metal Targets
- ///
- /// On bare-metal targets (such as microcontrollers or emulators), it's
- /// common to treat individual _CPU cores_ as a separate "threads". e.g:
- /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2]
- /// (note that TIDs cannot be zero).
- ///
- /// In this case, the `Tid` argument of `read/write_addrs` becomes quite
- /// relevant, as different cores may have different memory maps.
- ///
- /// ### Running in "Non-stop" mode
- ///
- /// At the moment, `gdbstub` only supports GDB's
- /// ["All-Stop" mode](https://sourceware.org/gdb/current/onlinedocs/gdb/All_002dStop-Mode.html),
- /// whereby _all_ threads must be stopped when returning from `resume`
- /// (not just the thread associated with the `ThreadStopReason`).
- fn resume(
- &mut self,
- default_resume_action: ResumeAction,
- gdb_interrupt: GdbInterrupt<'_>,
- ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
-
- /// Clear all previously set resume actions.
- fn clear_resume_actions(&mut self) -> Result<(), Self::Error>;
-
- /// Specify what action each thread should take when
- /// [`resume`](Self::resume) is called.
- ///
- /// A simple implementation of this method would simply update an internal
- /// `HashMap<Tid, ResumeAction>`.
- ///
- /// Aside from the four "base" resume actions handled by this method (i.e:
- /// `Step`, `Continue`, `StepWithSignal`, and `ContinueWithSignal`),
- /// there are also two additional resume actions which are only set if the
- /// target implements their corresponding protocol extension:
- ///
- /// Action | Protocol Extension
- /// ---------------------------|---------------------------
- /// Optimized [Range Stepping] | See [`support_range_step()`]
- /// "Stop" | Used in "Non-Stop" mode \*
- ///
- /// \* "Non-Stop" mode is currently unimplemented
- ///
- /// [Range Stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
- /// [`support_range_step()`]: Self::support_range_step
- fn set_resume_action(&mut self, tid: Tid, action: ResumeAction) -> Result<(), Self::Error>;
-
- /// Support for the optimized [range stepping] resume action.
- ///
- /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
- #[inline(always)]
- fn support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<Self>> {
- None
- }
-
- /// Support for [reverse stepping] a target.
- ///
- /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
- #[inline(always)]
- fn support_reverse_step(&mut self) -> Option<MultiThreadReverseStepOps<Self>> {
- None
- }
-
- /// Support for [reverse continuing] a target.
- ///
- /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
- #[inline(always)]
- fn support_reverse_cont(&mut self) -> Option<MultiThreadReverseContOps<Self>> {
- None
- }
-
+/// Base required debugging operations for multi threaded targets.
+pub trait MultiThreadBase: Target {
/// Read the target's registers.
///
/// If the registers could not be accessed, an appropriate non-fatal error
@@ -143,14 +28,18 @@ pub trait MultiThreadOps: Target {
) -> TargetResult<(), Self>;
/// Support for single-register access.
- /// See [`SingleRegisterAccess`](super::SingleRegisterAccess) for more
- /// details.
+ /// See [`SingleRegisterAccess`] for more details.
///
/// While this is an optional feature, it is **highly recommended** to
/// implement it when possible, as it can significantly improve performance
/// on certain architectures.
+ ///
+ /// [`SingleRegisterAccess`]:
+ /// super::single_register_access::SingleRegisterAccess
#[inline(always)]
- fn single_register_access(&mut self) -> Option<SingleRegisterAccessOps<Tid, Self>> {
+ fn support_single_register_access(
+ &mut self,
+ ) -> Option<super::single_register_access::SingleRegisterAccessOps<'_, Tid, Self>> {
None
}
@@ -182,6 +71,10 @@ pub trait MultiThreadOps: Target {
///
/// See [the section above](#bare-metal-targets) on implementing
/// thread-related methods on bare-metal (threadless) targets.
+ ///
+ /// _Note_: Implementors should mark this method as `#[inline(always)]`, as
+ /// this will result in better codegen (namely, by sidestepping any of the
+ /// `dyn FnMut` closure machinery).
fn list_active_threads(
&mut self,
thread_is_active: &mut dyn FnMut(Tid),
@@ -193,6 +86,7 @@ pub trait MultiThreadOps: Target {
/// uses `list_active_threads` to do a linear-search through all active
/// threads. On thread-heavy systems, it may be more efficient
/// to override this method with a more direct query.
+ #[allow(clippy::wrong_self_convention)] // requires breaking change to fix
fn is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error> {
let mut found = false;
self.list_active_threads(&mut |active_tid| {
@@ -202,59 +96,182 @@ pub trait MultiThreadOps: Target {
})?;
Ok(found)
}
+
+ /// Support for resuming the target (e.g: via `continue` or `step`)
+ #[inline(always)]
+ fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
+ None
+ }
+
+ /// Support for providing thread extra information.
+ #[inline(always)]
+ fn support_thread_extra_info(
+ &mut self,
+ ) -> Option<crate::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
+ None
+ }
}
-/// Target Extension - [Reverse continue] for multi threaded targets.
-///
-/// Reverse continue allows the target to run backwards until it reaches the end
-/// of the replay log.
-///
-/// [Reverse continue]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
-pub trait MultiThreadReverseCont: Target + MultiThreadOps {
- /// Reverse-continue the target.
- fn reverse_cont(
+/// Target extension - support for resuming multi threaded targets.
+pub trait MultiThreadResume: Target {
+ /// Resume execution on the target.
+ ///
+ /// Prior to calling `resume`, `gdbstub` will call `clear_resume_actions`,
+ /// followed by zero or more calls to the `set_resume_action_XXX` methods,
+ /// specifying any thread-specific resume actions.
+ ///
+ /// Upon returning from the `resume` method, the target being debugged
+ /// should be configured to run according to whatever resume actions the
+ /// GDB client had specified using any of the `set_resume_action_XXX`
+ /// methods.
+ ///
+ /// Any thread that wasn't explicitly resumed by a `set_resume_action_XXX`
+ /// method should be resumed as though it was resumed with
+ /// `set_resume_action_continue`.
+ ///
+ /// A basic target implementation only needs to implement support for
+ /// `set_resume_action_continue`, with all other resume actions requiring
+ /// their corresponding protocol extension to be implemented:
+ ///
+ /// Action | Protocol Extension
+ /// ----------------------------|------------------------------
+ /// Optimized [Single Stepping] | See [`support_single_step()`]
+ /// Optimized [Range Stepping] | See [`support_range_step()`]
+ /// "Stop" | Used in "Non-Stop" mode \*
+ ///
+ /// \* "Non-Stop" mode is currently unimplemented in `gdbstub`
+ ///
+ /// [Single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
+ /// [Range Stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
+ /// [`support_single_step()`]: Self::support_single_step
+ /// [`support_range_step()`]: Self::support_range_step
+ ///
+ /// # Additional Considerations
+ ///
+ /// ### Adjusting PC after a breakpoint is hit
+ ///
+ /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason)
+ /// notes the following:
+ ///
+ /// > On some architectures, such as x86, at the architecture level, when a
+ /// > breakpoint instruction executes the program counter points at the
+ /// > breakpoint address plus an offset. On such targets, the stub is
+ /// > responsible for adjusting the PC to point back at the breakpoint
+ /// > address.
+ ///
+ /// Omitting PC adjustment may result in unexpected execution flow and/or
+ /// breakpoints not appearing to work correctly.
+ ///
+ /// ### Bare-Metal Targets
+ ///
+ /// On bare-metal targets (such as microcontrollers or emulators), it's
+ /// common to treat individual _CPU cores_ as a separate "threads". e.g:
+ /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2]
+ /// (note that TIDs cannot be zero).
+ ///
+ /// In this case, the `Tid` argument of `read/write_addrs` becomes quite
+ /// relevant, as different cores may have different memory maps.
+ fn resume(&mut self) -> Result<(), Self::Error>;
+
+ /// Clear all previously set resume actions.
+ fn clear_resume_actions(&mut self) -> Result<(), Self::Error>;
+
+ /// Continue the specified thread.
+ ///
+ /// See the [`resume`](Self::resume) docs for information on when this is
+ /// called.
+ ///
+ /// The GDB client may also include a `signal` which should be passed to the
+ /// target.
+ fn set_resume_action_continue(
+ &mut self,
+ tid: Tid,
+ signal: Option<Signal>,
+ ) -> Result<(), Self::Error>;
+
+ /// Support for optimized [single stepping].
+ ///
+ /// [single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
+ #[inline(always)]
+ fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> {
+ None
+ }
+
+ /// Support for optimized [range stepping].
+ ///
+ /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
+ #[inline(always)]
+ fn support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<'_, Self>> {
+ None
+ }
+
+ /// Support for [reverse stepping] a target.
+ ///
+ /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
+ #[inline(always)]
+ fn support_reverse_step(
&mut self,
- gdb_interrupt: GdbInterrupt<'_>,
- ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
+ ) -> Option<super::reverse_exec::ReverseStepOps<'_, Tid, Self>> {
+ None
+ }
+
+ /// Support for [reverse continuing] a target.
+ ///
+ /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
+ #[inline(always)]
+ fn support_reverse_cont(
+ &mut self,
+ ) -> Option<super::reverse_exec::ReverseContOps<'_, Tid, Self>> {
+ None
+ }
}
-define_ext!(MultiThreadReverseContOps, MultiThreadReverseCont);
+define_ext!(MultiThreadResumeOps, MultiThreadResume);
-/// Target Extension - [Reverse stepping] for multi threaded targets.
-///
-/// Reverse stepping allows the target to run backwards by one step.
-///
-/// [Reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
-pub trait MultiThreadReverseStep: Target + MultiThreadOps {
- /// Reverse-step the specified [`Tid`].
- fn reverse_step(
+/// Target Extension - Optimized single stepping for multi threaded targets.
+/// See [`MultiThreadResume::support_single_step`].
+pub trait MultiThreadSingleStep: Target + MultiThreadResume {
+ /// [Single step] the specified target thread.
+ ///
+ /// Single stepping will step the target a single "step" - typically a
+ /// single instruction.
+ ///
+ /// The GDB client may also include a `signal` which should be passed to the
+ /// target.
+ ///
+ /// If your target does not support signals (e.g: the target is a bare-metal
+ /// microcontroller / emulator), the recommended behavior is to return a
+ /// target-specific fatal error
+ ///
+ /// [Single step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
+ fn set_resume_action_step(
&mut self,
tid: Tid,
- gdb_interrupt: GdbInterrupt<'_>,
- ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
+ signal: Option<Signal>,
+ ) -> Result<(), Self::Error>;
}
-define_ext!(MultiThreadReverseStepOps, MultiThreadReverseStep);
+define_ext!(MultiThreadSingleStepOps, MultiThreadSingleStep);
-/// Target Extension - Optimized [range stepping] for multi threaded targets.
-/// See [`MultiThreadOps::support_range_step`].
-///
-/// Range Stepping will step the target once, and keep stepping the target as
-/// long as execution remains between the specified start (inclusive) and end
-/// (exclusive) addresses, or another stop condition is met (e.g: a breakpoint
-/// it hit).
-///
-/// If the range is empty (`start` == `end`), then the action becomes
-/// equivalent to the ā€˜sā€™ action. In other words, single-step once, and
-/// report the stop (even if the stepped instruction jumps to start).
-///
-/// _Note:_ A stop reply may be sent at any point even if the PC is still
-/// within the stepping range; for example, it is valid to implement range
-/// stepping in a degenerate way as a single instruction step operation.
-///
-/// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
-pub trait MultiThreadRangeStepping: Target + MultiThreadOps {
- /// See [`MultiThreadOps::set_resume_action`].
+/// Target Extension - Optimized range stepping for multi threaded targets.
+/// See [`MultiThreadResume::support_range_step`].
+pub trait MultiThreadRangeStepping: Target + MultiThreadResume {
+ /// [Range step] the specified target thread.
+ ///
+ /// Range Stepping will step the target once, and keep stepping the target
+ /// as long as execution remains between the specified start (inclusive)
+ /// and end (exclusive) addresses, or another stop condition is met
+ /// (e.g: a breakpoint it hit).
+ ///
+ /// If the range is empty (`start` == `end`), then the action becomes
+ /// equivalent to the ā€˜sā€™ action. In other words, single-step once, and
+ /// report the stop (even if the stepped instruction jumps to start).
+ ///
+ /// _Note:_ A stop reply may be sent at any point even if the PC is still
+ /// within the stepping range; for example, it is valid to implement range
+ /// stepping in a degenerate way as a single instruction step operation.
+ ///
+ /// [Range step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
fn set_resume_action_range_step(
&mut self,
tid: Tid,
@@ -264,65 +281,3 @@ pub trait MultiThreadRangeStepping: Target + MultiThreadOps {
}
define_ext!(MultiThreadRangeSteppingOps, MultiThreadRangeStepping);
-
-/// Describes why a thread stopped.
-///
-/// Targets MUST only respond with stop reasons that correspond to IDETs that
-/// target has implemented.
-///
-/// 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 ThreadStopReason<U> {
- /// Completed the single-step request.
- DoneStep,
- /// `check_gdb_interrupt` returned `true`.
- GdbInterrupt,
- /// The process exited with the specified exit status.
- Exited(u8),
- /// The process terminated with the specified signal number.
- Terminated(u8),
- /// The program received a signal.
- Signal(u8),
- /// 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 {
- /// Which thread hit the watchpoint
- 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: [`MultiThreadReverseCont`] or [`MultiThreadReverseStep`].
- ///
- /// 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.
- ReplayLog(ReplayLogPosition),
-}