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