• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Base debugging operations for multi threaded targets.
2 
3 use crate::arch::Arch;
4 use crate::common::Signal;
5 use crate::common::Tid;
6 use crate::target::{Target, TargetResult};
7 
8 /// Base required debugging operations for multi threaded targets.
9 pub trait MultiThreadBase: Target {
10     /// Read the target's registers.
11     ///
12     /// If the registers could not be accessed, an appropriate non-fatal error
13     /// should be returned.
read_registers( &mut self, regs: &mut <Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>14     fn read_registers(
15         &mut self,
16         regs: &mut <Self::Arch as Arch>::Registers,
17         tid: Tid,
18     ) -> TargetResult<(), Self>;
19 
20     /// Write the target's registers.
21     ///
22     /// If the registers could not be accessed, an appropriate non-fatal error
23     /// should be returned.
write_registers( &mut self, regs: &<Self::Arch as Arch>::Registers, tid: Tid, ) -> TargetResult<(), Self>24     fn write_registers(
25         &mut self,
26         regs: &<Self::Arch as Arch>::Registers,
27         tid: Tid,
28     ) -> TargetResult<(), Self>;
29 
30     /// Support for single-register access.
31     /// See [`SingleRegisterAccess`] for more details.
32     ///
33     /// While this is an optional feature, it is **highly recommended** to
34     /// implement it when possible, as it can significantly improve performance
35     /// on certain architectures.
36     ///
37     /// [`SingleRegisterAccess`]:
38     /// super::single_register_access::SingleRegisterAccess
39     #[inline(always)]
support_single_register_access( &mut self, ) -> Option<super::single_register_access::SingleRegisterAccessOps<'_, Tid, Self>>40     fn support_single_register_access(
41         &mut self,
42     ) -> Option<super::single_register_access::SingleRegisterAccessOps<'_, Tid, Self>> {
43         None
44     }
45 
46     /// Read bytes from the specified address range.
47     ///
48     /// If the requested address range could not be accessed (e.g: due to
49     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
50     /// error should be returned.
read_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &mut [u8], tid: Tid, ) -> TargetResult<(), Self>51     fn read_addrs(
52         &mut self,
53         start_addr: <Self::Arch as Arch>::Usize,
54         data: &mut [u8],
55         tid: Tid,
56     ) -> TargetResult<(), Self>;
57 
58     /// Write bytes to the specified address range.
59     ///
60     /// If the requested address range could not be accessed (e.g: due to
61     /// MMU protection, unhanded page fault, etc...), an appropriate non-fatal
62     /// error should be returned.
write_addrs( &mut self, start_addr: <Self::Arch as Arch>::Usize, data: &[u8], tid: Tid, ) -> TargetResult<(), Self>63     fn write_addrs(
64         &mut self,
65         start_addr: <Self::Arch as Arch>::Usize,
66         data: &[u8],
67         tid: Tid,
68     ) -> TargetResult<(), Self>;
69 
70     /// List all currently active threads.
71     ///
72     /// See [the section above](#bare-metal-targets) on implementing
73     /// thread-related methods on bare-metal (threadless) targets.
74     ///
75     /// _Note_: Implementors should mark this method as `#[inline(always)]`, as
76     /// this will result in better codegen (namely, by sidestepping any of the
77     /// `dyn FnMut` closure machinery).
list_active_threads( &mut self, thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), Self::Error>78     fn list_active_threads(
79         &mut self,
80         thread_is_active: &mut dyn FnMut(Tid),
81     ) -> Result<(), Self::Error>;
82 
83     /// Check if the specified thread is alive.
84     ///
85     /// As a convenience, this method provides a default implementation which
86     /// uses `list_active_threads` to do a linear-search through all active
87     /// threads. On thread-heavy systems, it may be more efficient
88     /// to override this method with a more direct query.
89     #[allow(clippy::wrong_self_convention)] // requires breaking change to fix
is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error>90     fn is_thread_alive(&mut self, tid: Tid) -> Result<bool, Self::Error> {
91         let mut found = false;
92         self.list_active_threads(&mut |active_tid| {
93             if tid == active_tid {
94                 found = true;
95             }
96         })?;
97         Ok(found)
98     }
99 
100     /// Support for resuming the target (e.g: via `continue` or `step`)
101     #[inline(always)]
support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>>102     fn support_resume(&mut self) -> Option<MultiThreadResumeOps<'_, Self>> {
103         None
104     }
105 
106     /// Support for providing thread extra information.
107     #[inline(always)]
support_thread_extra_info( &mut self, ) -> Option<crate::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>>108     fn support_thread_extra_info(
109         &mut self,
110     ) -> Option<crate::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
111         None
112     }
113 }
114 
115 /// Target extension - support for resuming multi threaded targets.
116 pub trait MultiThreadResume: Target {
117     /// Resume execution on the target.
118     ///
119     /// Prior to calling `resume`, `gdbstub` will call `clear_resume_actions`,
120     /// followed by zero or more calls to the `set_resume_action_XXX` methods,
121     /// specifying any thread-specific resume actions.
122     ///
123     /// Upon returning from the `resume` method, the target being debugged
124     /// should be configured to run according to whatever resume actions the
125     /// GDB client had specified using any of the `set_resume_action_XXX`
126     /// methods.
127     ///
128     /// Any thread that wasn't explicitly resumed by a `set_resume_action_XXX`
129     /// method should be resumed as though it was resumed with
130     /// `set_resume_action_continue`.
131     ///
132     /// A basic target implementation only needs to implement support for
133     /// `set_resume_action_continue`, with all other resume actions requiring
134     /// their corresponding protocol extension to be implemented:
135     ///
136     /// Action                      | Protocol Extension
137     /// ----------------------------|------------------------------
138     /// Optimized [Single Stepping] | See [`support_single_step()`]
139     /// Optimized [Range Stepping]  | See [`support_range_step()`]
140     /// "Stop"                      | Used in "Non-Stop" mode \*
141     ///
142     /// \* "Non-Stop" mode is currently unimplemented in `gdbstub`
143     ///
144     /// [Single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
145     /// [Range Stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
146     /// [`support_single_step()`]: Self::support_single_step
147     /// [`support_range_step()`]: Self::support_range_step
148     ///
149     /// # Additional Considerations
150     ///
151     /// ### Adjusting PC after a breakpoint is hit
152     ///
153     /// The [GDB remote serial protocol documentation](https://sourceware.org/gdb/current/onlinedocs/gdb/Stop-Reply-Packets.html#swbreak-stop-reason)
154     /// notes the following:
155     ///
156     /// > On some architectures, such as x86, at the architecture level, when a
157     /// > breakpoint instruction executes the program counter points at the
158     /// > breakpoint address plus an offset. On such targets, the stub is
159     /// > responsible for adjusting the PC to point back at the breakpoint
160     /// > address.
161     ///
162     /// Omitting PC adjustment may result in unexpected execution flow and/or
163     /// breakpoints not appearing to work correctly.
164     ///
165     /// ### Bare-Metal Targets
166     ///
167     /// On bare-metal targets (such as microcontrollers or emulators), it's
168     /// common to treat individual _CPU cores_ as a separate "threads". e.g:
169     /// in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2]
170     /// (note that TIDs cannot be zero).
171     ///
172     /// In this case, the `Tid` argument of `read/write_addrs` becomes quite
173     /// relevant, as different cores may have different memory maps.
resume(&mut self) -> Result<(), Self::Error>174     fn resume(&mut self) -> Result<(), Self::Error>;
175 
176     /// Clear all previously set resume actions.
clear_resume_actions(&mut self) -> Result<(), Self::Error>177     fn clear_resume_actions(&mut self) -> Result<(), Self::Error>;
178 
179     /// Continue the specified thread.
180     ///
181     /// See the [`resume`](Self::resume) docs for information on when this is
182     /// called.
183     ///
184     /// The GDB client may also include a `signal` which should be passed to the
185     /// target.
set_resume_action_continue( &mut self, tid: Tid, signal: Option<Signal>, ) -> Result<(), Self::Error>186     fn set_resume_action_continue(
187         &mut self,
188         tid: Tid,
189         signal: Option<Signal>,
190     ) -> Result<(), Self::Error>;
191 
192     /// Support for optimized [single stepping].
193     ///
194     /// [single stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
195     #[inline(always)]
support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>>196     fn support_single_step(&mut self) -> Option<MultiThreadSingleStepOps<'_, Self>> {
197         None
198     }
199 
200     /// Support for optimized [range stepping].
201     ///
202     /// [range stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
203     #[inline(always)]
support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<'_, Self>>204     fn support_range_step(&mut self) -> Option<MultiThreadRangeSteppingOps<'_, Self>> {
205         None
206     }
207 
208     /// Support for [reverse stepping] a target.
209     ///
210     /// [reverse stepping]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
211     #[inline(always)]
support_reverse_step( &mut self, ) -> Option<super::reverse_exec::ReverseStepOps<'_, Tid, Self>>212     fn support_reverse_step(
213         &mut self,
214     ) -> Option<super::reverse_exec::ReverseStepOps<'_, Tid, Self>> {
215         None
216     }
217 
218     /// Support for [reverse continuing] a target.
219     ///
220     /// [reverse continuing]: https://sourceware.org/gdb/current/onlinedocs/gdb/Reverse-Execution.html
221     #[inline(always)]
support_reverse_cont( &mut self, ) -> Option<super::reverse_exec::ReverseContOps<'_, Tid, Self>>222     fn support_reverse_cont(
223         &mut self,
224     ) -> Option<super::reverse_exec::ReverseContOps<'_, Tid, Self>> {
225         None
226     }
227 }
228 
229 define_ext!(MultiThreadResumeOps, MultiThreadResume);
230 
231 /// Target Extension - Optimized single stepping for multi threaded targets.
232 /// See [`MultiThreadResume::support_single_step`].
233 pub trait MultiThreadSingleStep: Target + MultiThreadResume {
234     /// [Single step] the specified target thread.
235     ///
236     /// Single stepping will step the target a single "step" - typically a
237     /// single instruction.
238     ///
239     /// The GDB client may also include a `signal` which should be passed to the
240     /// target.
241     ///
242     /// If your target does not support signals (e.g: the target is a bare-metal
243     /// microcontroller / emulator), the recommended behavior is to return a
244     /// target-specific fatal error
245     ///
246     /// [Single step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#index-stepi
set_resume_action_step( &mut self, tid: Tid, signal: Option<Signal>, ) -> Result<(), Self::Error>247     fn set_resume_action_step(
248         &mut self,
249         tid: Tid,
250         signal: Option<Signal>,
251     ) -> Result<(), Self::Error>;
252 }
253 
254 define_ext!(MultiThreadSingleStepOps, MultiThreadSingleStep);
255 
256 /// Target Extension - Optimized range stepping for multi threaded targets.
257 /// See [`MultiThreadResume::support_range_step`].
258 pub trait MultiThreadRangeStepping: Target + MultiThreadResume {
259     /// [Range step] the specified target thread.
260     ///
261     /// Range Stepping will step the target once, and keep stepping the target
262     /// as long as execution remains between the specified start (inclusive)
263     /// and end (exclusive) addresses, or another stop condition is met
264     /// (e.g: a breakpoint it hit).
265     ///
266     /// If the range is empty (`start` == `end`), then the action becomes
267     /// equivalent to the ‘s’ action. In other words, single-step once, and
268     /// report the stop (even if the stepped instruction jumps to start).
269     ///
270     /// _Note:_ A stop reply may be sent at any point even if the PC is still
271     /// within the stepping range; for example, it is valid to implement range
272     /// stepping in a degenerate way as a single instruction step operation.
273     ///
274     /// [Range step]: https://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#range-stepping
set_resume_action_range_step( &mut self, tid: Tid, start: <Self::Arch as Arch>::Usize, end: <Self::Arch as Arch>::Usize, ) -> Result<(), Self::Error>275     fn set_resume_action_range_step(
276         &mut self,
277         tid: Tid,
278         start: <Self::Arch as Arch>::Usize,
279         end: <Self::Arch as Arch>::Usize,
280     ) -> Result<(), Self::Error>;
281 }
282 
283 define_ext!(MultiThreadRangeSteppingOps, MultiThreadRangeStepping);
284