1 //! The core [`GdbStub`] type, used to drive a GDB debugging session for a
2 //! particular [`Target`] over a given [`Connection`].
3 
4 pub use builder::GdbStubBuilder;
5 pub use builder::GdbStubBuilderError;
6 pub use core_impl::DisconnectReason;
7 pub use error::GdbStubError;
8 pub use stop_reason::BaseStopReason;
9 pub use stop_reason::IntoStopReason;
10 pub use stop_reason::MultiThreadStopReason;
11 pub use stop_reason::SingleThreadStopReason;
12 
13 mod builder;
14 mod core_impl;
15 mod error;
16 mod stop_reason;
17 
18 pub mod state_machine;
19 
20 use self::error::InternalError;
21 use crate::conn::Connection;
22 use crate::conn::ConnectionExt;
23 use crate::target::Target;
24 use managed::ManagedSlice;
25 
26 /// Types and traits related to the [`GdbStub::run_blocking`] interface.
27 pub mod run_blocking {
28     use super::*;
29     use crate::conn::ConnectionExt;
30 
31     /// A set of user-provided methods required to run a GDB debugging session
32     /// using the [`GdbStub::run_blocking`] method.
33     ///
34     /// Reminder: to use `gdbstub` in a non-blocking manner (e.g: via
35     /// async/await, unix polling, from an interrupt handler, etc...) you will
36     /// need to interface with the
37     /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API
38     /// directly.
39     pub trait BlockingEventLoop {
40         /// The Target being driven.
41         type Target: Target;
42         /// Connection being used to drive the target.
43         type Connection: ConnectionExt;
44 
45         /// Which variant of the `StopReason` type should be used. Single
46         /// threaded targets should use [`SingleThreadStopReason`], whereas
47         /// multi threaded targets should use [`MultiThreadStopReason`].
48         ///
49         /// [`SingleThreadStopReason`]: crate::stub::SingleThreadStopReason
50         /// [`MultiThreadStopReason`]: crate::stub::MultiThreadStopReason
51         type StopReason: IntoStopReason<Self::Target>;
52 
53         /// Invoked immediately after the target's `resume` method has been
54         /// called. The implementation should block until either the target
55         /// reports a stop reason, or if new data was sent over the connection.
56         ///
57         /// The specific mechanism to "select" between these two events is
58         /// implementation specific. Some examples might include: `epoll`,
59         /// `select!` across multiple event channels, periodic polling, etc...
wait_for_stop_reason( target: &mut Self::Target, conn: &mut Self::Connection, ) -> Result< Event<Self::StopReason>, WaitForStopReasonError< <Self::Target as Target>::Error, <Self::Connection as Connection>::Error, >, >60         fn wait_for_stop_reason(
61             target: &mut Self::Target,
62             conn: &mut Self::Connection,
63         ) -> Result<
64             Event<Self::StopReason>,
65             WaitForStopReasonError<
66                 <Self::Target as Target>::Error,
67                 <Self::Connection as Connection>::Error,
68             >,
69         >;
70 
71         /// Invoked when the GDB client sends a Ctrl-C interrupt.
72         ///
73         /// Depending on how the target is implemented, it may or may not make
74         /// sense to immediately return a stop reason as part of handling the
75         /// Ctrl-C interrupt. e.g: in some cases, it may be better to send the
76         /// target a signal upon receiving a Ctrl-C interrupt _without_
77         /// immediately sending a stop reason, and instead deferring the stop
78         /// reason to some later point in the target's execution.
79         ///
80         /// _Suggestion_: If you're unsure which stop reason to report,
81         /// [`BaseStopReason::Signal(Signal::SIGINT)`] is a sensible default.
82         ///
83         /// [`BaseStopReason::Signal(Signal::SIGINT)`]:
84         /// crate::stub::BaseStopReason::Signal
on_interrupt( target: &mut Self::Target, ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error>85         fn on_interrupt(
86             target: &mut Self::Target,
87         ) -> Result<Option<Self::StopReason>, <Self::Target as Target>::Error>;
88     }
89 
90     /// Returned by the `wait_for_stop_reason` closure in
91     /// [`GdbStub::run_blocking`]
92     pub enum Event<StopReason> {
93         /// GDB Client sent data while the target was running.
94         IncomingData(u8),
95         /// The target has stopped.
96         TargetStopped(StopReason),
97     }
98 
99     /// Error value returned by the `wait_for_stop_reason` closure in
100     /// [`GdbStub::run_blocking`]
101     pub enum WaitForStopReasonError<T, C> {
102         /// A fatal target error has occurred.
103         Target(T),
104         /// A fatal connection error has occurred.
105         Connection(C),
106     }
107 }
108 
109 /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given
110 /// [`Connection`].
111 pub struct GdbStub<'a, T: Target, C: Connection> {
112     conn: C,
113     packet_buffer: ManagedSlice<'a, u8>,
114     inner: core_impl::GdbStubImpl<T, C>,
115 }
116 
117 impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> {
118     /// Create a [`GdbStubBuilder`] using the provided Connection.
builder(conn: C) -> GdbStubBuilder<'a, T, C>119     pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> {
120         GdbStubBuilder::new(conn)
121     }
122 
123     /// Create a new `GdbStub` using the provided connection.
124     ///
125     /// _Note:_ `new` is only available when the `alloc` feature is enabled, as
126     /// it will use a dynamically allocated `Vec` as a packet buffer.
127     ///
128     /// For fine-grained control over various `GdbStub` options, including the
129     /// ability to specify a fixed-size buffer, use the [`GdbStub::builder`]
130     /// method instead.
131     #[cfg(feature = "alloc")]
new(conn: C) -> GdbStub<'a, T, C>132     pub fn new(conn: C) -> GdbStub<'a, T, C> {
133         GdbStubBuilder::new(conn).build().unwrap()
134     }
135 
136     /// (Quickstart) Start a GDB remote debugging session using a blocking event
137     /// loop.
138     ///
139     /// This method provides a quick and easy way to get up and running with
140     /// `gdbstub` without directly having to immediately interface with the
141     /// lower-level [state-machine](state_machine::GdbStubStateMachine)
142     /// based interface.
143     ///
144     /// Instead, an implementation simply needs to provide a implementation of
145     /// [`run_blocking::BlockingEventLoop`], which is a simplified set
146     /// of methods describing how to drive the target.
147     ///
148     /// `GdbStub::run_blocking` returns once the GDB client closes the debugging
149     /// session, or if the target triggers a disconnect.
150     ///
151     /// Note that this implementation is **blocking**, which many not be
152     /// preferred (or suitable) in all cases. To use `gdbstub` in a non-blocking
153     /// manner (e.g: via async/await, unix polling, from an interrupt handler,
154     /// etc...) you will need to interface with the underlying
155     /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) API
156     /// directly.
run_blocking<E>( self, target: &mut T, ) -> Result<DisconnectReason, GdbStubError<T::Error, C::Error>> where C: ConnectionExt, E: run_blocking::BlockingEventLoop<Target = T, Connection = C>,157     pub fn run_blocking<E>(
158         self,
159         target: &mut T,
160     ) -> Result<DisconnectReason, GdbStubError<T::Error, C::Error>>
161     where
162         C: ConnectionExt,
163         E: run_blocking::BlockingEventLoop<Target = T, Connection = C>,
164     {
165         let mut gdb = self.run_state_machine(target)?;
166         loop {
167             gdb = match gdb {
168                 state_machine::GdbStubStateMachine::Idle(mut gdb) => {
169                     // needs more data, so perform a blocking read on the connection
170                     let byte = gdb.borrow_conn().read().map_err(InternalError::conn_read)?;
171                     gdb.incoming_data(target, byte)?
172                 }
173 
174                 state_machine::GdbStubStateMachine::Disconnected(gdb) => {
175                     // run_blocking keeps things simple, and doesn't expose a way to re-use the
176                     // state machine
177                     break Ok(gdb.get_reason());
178                 }
179 
180                 state_machine::GdbStubStateMachine::CtrlCInterrupt(gdb) => {
181                     // defer to the implementation on how it wants to handle the interrupt
182                     let stop_reason =
183                         E::on_interrupt(target).map_err(InternalError::TargetError)?;
184                     gdb.interrupt_handled(target, stop_reason)?
185                 }
186 
187                 state_machine::GdbStubStateMachine::Running(mut gdb) => {
188                     use run_blocking::Event as BlockingEventLoopEvent;
189                     use run_blocking::WaitForStopReasonError;
190 
191                     // block waiting for the target to return a stop reason
192                     let event = E::wait_for_stop_reason(target, gdb.borrow_conn());
193                     match event {
194                         Ok(BlockingEventLoopEvent::TargetStopped(stop_reason)) => {
195                             gdb.report_stop(target, stop_reason)?
196                         }
197 
198                         Ok(BlockingEventLoopEvent::IncomingData(byte)) => {
199                             gdb.incoming_data(target, byte)?
200                         }
201 
202                         Err(WaitForStopReasonError::Target(e)) => {
203                             break Err(InternalError::TargetError(e).into());
204                         }
205                         Err(WaitForStopReasonError::Connection(e)) => {
206                             break Err(InternalError::conn_read(e).into());
207                         }
208                     }
209                 }
210             }
211         }
212     }
213 
214     /// Starts a GDB remote debugging session, converting this instance of
215     /// `GdbStub` into a
216     /// [`GdbStubStateMachine`](state_machine::GdbStubStateMachine) that is
217     /// ready to receive data.
run_state_machine( mut self, target: &mut T, ) -> Result<state_machine::GdbStubStateMachine<'a, T, C>, GdbStubError<T::Error, C::Error>>218     pub fn run_state_machine(
219         mut self,
220         target: &mut T,
221     ) -> Result<state_machine::GdbStubStateMachine<'a, T, C>, GdbStubError<T::Error, C::Error>>
222     {
223         // Check if the target hasn't explicitly opted into implicit sw breakpoints
224         {
225             let support_software_breakpoints = target
226                 .support_breakpoints()
227                 .map(|ops| ops.support_sw_breakpoint().is_some())
228                 .unwrap_or(false);
229 
230             if !support_software_breakpoints && !target.guard_rail_implicit_sw_breakpoints() {
231                 return Err(InternalError::ImplicitSwBreakpoints.into());
232             }
233         }
234 
235         // Perform any connection initialization
236         {
237             self.conn
238                 .on_session_start()
239                 .map_err(InternalError::conn_init)?;
240         }
241 
242         Ok(state_machine::GdbStubStateMachineInner::from_plain_gdbstub(self).into())
243     }
244 }
245