• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Base debugging operations for multi threaded targets.
2 
3 use crate::arch::Arch;
4 use crate::common::*;
5 use crate::target::ext::breakpoints::WatchKind;
6 use crate::target::{Target, TargetResult};
7 
8 // Convenient re-exports
9 pub use super::ResumeAction;
10 
11 /// Selects a thread corresponding to a ResumeAction.
12 // NOTE: this is a subset of the internal `IdKind` type, albeit without an `Any` variant. Selecting
13 // `Any` thread is something that's handled by `gdbstub` internally, and shouldn't be exposed to the
14 // end user.
15 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
16 pub enum TidSelector {
17     /// Thread with a specific ID.
18     WithID(Tid),
19     /// All (other) threads.
20     All,
21 }
22 
23 /// Base debugging operations for multi threaded targets.
24 #[allow(clippy::type_complexity)]
25 pub trait MultiThreadOps: Target {
26     /// Resume execution on the target.
27     ///
28     /// `actions` is an iterator over `(TidSelector, ResumeAction)` pairs which
29     /// specify how various threads should be resumed (i.e: single-step vs.
30     /// resume). It is _guaranteed_ to contain at least one action. It is not
31     /// guaranteed to be exhaustive over all live threads, and any threads
32     /// without a corresponding `TidSelector` should be left in the same state
33     /// (if possible).
34     ///
35     /// The `check_gdb_interrupt` callback can be invoked to check if GDB sent
36     /// an Interrupt packet (i.e: the user pressed Ctrl-C). It's recommended to
37     /// invoke this callback every-so-often while the system is running (e.g:
38     /// every X cycles/milliseconds). Periodically checking for incoming
39     /// interrupt packets is _not_ required, but it is _recommended_.
40     ///
41     /// # Implementation requirements
42     ///
43     /// These requirements cannot be satisfied by `gdbstub` internally, and must
44     /// be handled on a per-target basis.
45     ///
46     /// ### Adjusting PC after a breakpoint is hit
47     ///
48     /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason)
49     /// notes the following:
50     ///
51     /// > On some architectures, such as x86, at the architecture level, when a
52     /// > breakpoint instruction executes the program counter points at the
53     /// > breakpoint address plus an offset. On such targets, the stub is
54     /// > responsible for adjusting the PC to point back at the breakpoint
55     /// > address.
56     ///
57     /// Omitting PC adjustment may result in unexpected execution flow and/or
58     /// breakpoints not appearing to work correctly.
59     ///
60     /// # Additional Considerations
61     ///
62     /// ### "Non-stop" mode
63     ///
64     /// At the moment, `gdbstub` only supports GDB's
65     /// ["All-Stop" mode](https://sourceware.org/gdb/current/onlinedocs/gdb/All_002dStop-Mode.html),
66     /// whereby _all_ threads should be stopped when returning from `resume`
67     /// (not just the thread associated with the `ThreadStopReason`).
68     ///
69     /// ### Bare-Metal Targets
70     ///
71     /// On bare-metal targets (such as microcontrollers or emulators), it's
72     /// common to treat individual _CPU cores_ as a separate "threads". e.g:
73     /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2]
74     /// (note that TIDs cannot be zero).
75     ///
76     /// In this case, the `Tid` argument of `read/write_addrs` becomes quite
77     /// relevant, as different cores may have different memory maps.
resume( &mut self, actions: Actions<'_>, check_gdb_interrupt: &mut dyn FnMut() -> bool, ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>78     fn resume(
79         &mut self,
80         actions: Actions<'_>,
81         check_gdb_interrupt: &mut dyn FnMut() -> bool,
82     ) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
83 
84     /// Read the target's registers.
85     ///
86     /// If the registers could not be accessed, an appropriate non-fatal error
87     /// should be returned.
read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>88     fn read_registers(
89         &mut self,
90         regs: &mut <Self::Arch as Arch>::Registers,
91         tid: Tid,
92     ) -> TargetResult<(), Self>;
93 
94     /// Write the target's registers.
95     ///
96     /// If the registers could not be accessed, an appropriate non-fatal error
97     /// should be returned.
write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>98     fn write_registers(
99         &mut self,
100         regs: &<Self::Arch as Arch>::Registers,
101         tid: Tid,
102     ) -> TargetResult<(), Self>;
103 
104     /// Read to a single register on the target.
105     ///
106     /// Implementations should write the value of the register using target's
107     /// native byte order in the buffer `dst`.
108     ///
109     /// If the requested register could not be accessed, an appropriate
110     /// non-fatal error should be returned.
111     ///
112     /// _Note:_ This method includes a stubbed default implementation which
113     /// simply returns `Ok(())`. This is due to the fact that several built-in
114     /// `arch` implementations haven't been updated with proper `RegId`
115     /// implementations.
read_register( &mut self, reg_id: <Self::Arch as Arch>::RegId, dst: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>116     fn read_register(
117         &mut self,
118         reg_id: <Self::Arch as Arch>::RegId,
119         dst: &mut [u8],
120         tid: Tid,
121     ) -> TargetResult<(), Self> {
122         let _ = (reg_id, dst, tid);
123         Ok(())
124     }
125 
126     /// Write from a single register on the target.
127     ///
128     /// The `val` buffer contains the new value of the register in the target's
129     /// native byte order. It is guaranteed to be the exact length as the target
130     /// register.
131     ///
132     /// If the requested register could not be accessed, an appropriate
133     /// non-fatal error should be returned.
134     ///
135     /// _Note:_ This method includes a stubbed default implementation which
136     /// simply returns `Ok(())`. This is due to the fact that several built-in
137     /// `arch` implementations haven't been updated with proper `RegId`
138     /// implementations.
write_register( &mut self, reg_id: <Self::Arch as Arch>::RegId, val: &[u8], tid: Tid, ) -> TargetResult<(), Self>139     fn write_register(
140         &mut self,
141         reg_id: <Self::Arch as Arch>::RegId,
142         val: &[u8],
143         tid: Tid,
144     ) -> TargetResult<(), Self> {
145         let _ = (reg_id, val, tid);
146         Ok(())
147     }
148 
149     /// Read bytes from the specified address range.
150     ///
151     /// If the requested address range could not be accessed (e.g: due to
152     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
153     /// error should be returned.
read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>154     fn read_addrs(
155         &mut self,
156         start_addr: <Self::Arch as Arch>::Usize,
157         data: &mut [u8],
158         tid: Tid,
159     ) -> TargetResult<(), Self>;
160 
161     /// Write bytes to the specified address range.
162     ///
163     /// If the requested address range could not be accessed (e.g: due to
164     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
165     /// error should be returned.
write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], tid: Tid, ) -> TargetResult<(), Self>166     fn write_addrs(
167         &mut self,
168         start_addr: <Self::Arch as Arch>::Usize,
169         data: &[u8],
170         tid: Tid,
171     ) -> TargetResult<(), Self>;
172 
173     /// List all currently active threads.
174     ///
175     /// See [the section above](#bare-metal-targets) on implementing
176     /// thread-related methods on bare-metal (threadless) targets.
list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>177     fn list_active_threads(
178         &mut self,
179         thread_is_active: &mut dyn FnMut(Tid),
180     ) -> Result<(), Self::Error>;
181 
182     /// Check if the specified thread is alive.
183     ///
184     /// As a convenience, this method provides a default implementation which
185     /// uses `list_active_threads` to do a linear-search through all active
186     /// threads. On thread-heavy systems, it may be more efficient
187     /// to override this method with a more direct query.
is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error>188     fn is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error> {
189         let mut found = false;
190         self.list_active_threads(&mut |active_tid| {
191             if tid == active_tid {
192                 found = true;
193             }
194         })?;
195         Ok(found)
196     }
197 }
198 
199 /// Describes why a thread stopped.
200 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
201 #[non_exhaustive]
202 pub enum ThreadStopReason<U> {
203     /// Completed the single-step request.
204     DoneStep,
205     /// `check_gdb_interrupt` returned `true`
206     GdbInterrupt,
207     /// Halted
208     Halted,
209     /// A thread hit a software breakpoint (e.g. due to a trap instruction).
210     ///
211     /// NOTE: This does not necessarily have to be a breakpoint configured by
212     /// the client/user of the current GDB session.
213     SwBreak(Tid),
214     /// A thread hit a hardware breakpoint.
215     HwBreak(Tid),
216     /// A thread hit a watchpoint.
217     Watch {
218         /// Which thread hit the watchpoint
219         tid: Tid,
220         /// Kind of watchpoint that was hit
221         kind: WatchKind,
222         /// Address of watched memory
223         addr: U,
224     },
225     /// The program received a signal
226     Signal(u8),
227 }
228 
229 /// An iterator of `(TidSelector, ResumeAction)` used to specify how threads
230 /// should be resumed when running in multi threaded mode. It is _guaranteed_ to
231 /// contain at least one action.
232 ///
233 /// See the documentation for
234 /// [`Target::resume`](trait.Target.html#tymethod.resume) for more details.
235 pub struct Actions<'a> {
236     inner: &'a mut dyn Iterator<Item = (TidSelector, ResumeAction)>,
237 }
238 
239 impl core::fmt::Debug for Actions<'_> {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result240     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
241         write!(f, "Actions {{ .. }}")
242     }
243 }
244 
245 impl Actions<'_> {
new(iter: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>) -> Actions<'_>246     pub(crate) fn new(iter: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>) -> Actions<'_> {
247         Actions { inner: iter }
248     }
249 }
250 
251 impl Iterator for Actions<'_> {
252     type Item = (TidSelector, ResumeAction);
next(&mut self) -> Option<Self::Item>253     fn next(&mut self) -> Option<Self::Item> {
254         self.inner.next()
255     }
256 }
257