1 use crate::protocol::PacketParseError; 2 use crate::protocol::ResponseWriterError; 3 use crate::util::managed_vec::CapacityError; 4 use core::fmt::Debug; 5 use core::fmt::Display; 6 use core::fmt::{self}; 7 8 /// An error that may occur while interacting with a 9 /// [`Connection`](crate::conn::Connection). 10 #[derive(Debug)] 11 pub enum ConnectionErrorKind { 12 /// Error initializing the session. 13 Init, 14 /// Error reading data. 15 Read, 16 /// Error writing data. 17 Write, 18 } 19 20 #[derive(Debug)] 21 pub(crate) enum InternalError<T, C> { 22 /// Connection Error 23 Connection(C, ConnectionErrorKind), 24 /// Target encountered a fatal error. 25 TargetError(T), 26 27 ClientSentNack, 28 PacketBufferOverflow, 29 PacketParse(PacketParseError), 30 PacketUnexpected, 31 TargetMismatch, 32 UnsupportedStopReason, 33 UnexpectedStepPacket, 34 ImplicitSwBreakpoints, 35 // DEVNOTE: this is a temporary workaround for something that can and should 36 // be caught at compile time via IDETs. That said, since i'm not sure when 37 // I'll find the time to cut a breaking release of gdbstub, I'd prefer to 38 // push out this feature as a non-breaking change now. 39 MissingCurrentActivePidImpl, 40 41 // Internal - A non-fatal error occurred (with errno-style error code) 42 // 43 // This "dummy" error is required as part of the internal 44 // `TargetResultExt::handle_error()` machinery, and will never be 45 // propagated up to the end user. 46 #[doc(hidden)] 47 NonFatalError(u8), 48 } 49 50 impl<T, C> InternalError<T, C> { conn_read(e: C) -> Self51 pub fn conn_read(e: C) -> Self { 52 InternalError::Connection(e, ConnectionErrorKind::Read) 53 } 54 conn_write(e: C) -> Self55 pub fn conn_write(e: C) -> Self { 56 InternalError::Connection(e, ConnectionErrorKind::Write) 57 } 58 conn_init(e: C) -> Self59 pub fn conn_init(e: C) -> Self { 60 InternalError::Connection(e, ConnectionErrorKind::Init) 61 } 62 } 63 64 impl<T, C> From<ResponseWriterError<C>> for InternalError<T, C> { from(e: ResponseWriterError<C>) -> Self65 fn from(e: ResponseWriterError<C>) -> Self { 66 InternalError::Connection(e.0, ConnectionErrorKind::Write) 67 } 68 } 69 70 // these macros are used to keep the docs and `Display` impl in-sync 71 72 macro_rules! unsupported_stop_reason { 73 () => { 74 "User error: cannot report stop reason without also implementing its corresponding IDET" 75 }; 76 } 77 78 macro_rules! unexpected_step_packet { 79 () => { 80 "Received an unexpected `step` request. This is most-likely due to this GDB client bug: <https://sourceware.org/bugzilla/show_bug.cgi?id=28440>" 81 }; 82 } 83 84 /// An error which may occur during a GDB debugging session. 85 /// 86 /// ## Additional Notes 87 /// 88 /// `GdbStubError`'s inherent `Display` impl typically contains enough context 89 /// for users to understand why the error occurred. 90 /// 91 /// That said, there are a few instances where the error condition requires 92 /// additional context. 93 /// 94 /// * * * 95 #[doc = concat!("_", unsupported_stop_reason!(), "_")] 96 /// 97 /// This is a not a bug with `gdbstub`. Rather, this is indicative of a bug in 98 /// your `gdbstub` integration. 99 /// 100 /// Certain stop reasons can only be used when their associated protocol feature 101 /// has been implemented. e.g: a Target cannot return a `StopReason::HwBreak` if 102 /// the hardware breakpoints IDET hasn't been implemented. 103 /// 104 /// Please double-check that you've implemented all the necessary `supports_` 105 /// methods related to the stop reason you're trying to report. 106 /// 107 /// * * * 108 #[doc = concat!("_", unexpected_step_packet!(), "_")] 109 /// 110 /// Unfortunately, there's nothing `gdbstub` can do to work around this bug. 111 /// 112 /// Until the issue is fixed upstream, certain architectures are essentially 113 /// forced to manually implement single-step support. 114 #[derive(Debug)] 115 pub struct GdbStubError<T, C> { 116 kind: InternalError<T, C>, 117 } 118 119 impl<T, C> Display for GdbStubError<T, C> 120 where 121 C: Display, 122 T: Display, 123 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 125 use self::InternalError::*; 126 const CONTEXT: &str = "See the `GdbStubError` docs for more details"; 127 match &self.kind { 128 Connection(e, ConnectionErrorKind::Init) => write!(f, "Connection Error while initializing the session: {}", e), 129 Connection(e, ConnectionErrorKind::Read) => write!(f, "Connection Error while reading request: {}", e), 130 Connection(e, ConnectionErrorKind::Write) => write!(f, "Connection Error while writing response: {}", e), 131 ClientSentNack => write!(f, "Client nack'd the last packet, but `gdbstub` doesn't implement re-transmission."), 132 PacketBufferOverflow => write!(f, "Received an oversized packet (did not fit in provided packet buffer)"), 133 PacketParse(e) => write!(f, "Failed to parse packet into a valid command: {:?}", e), 134 PacketUnexpected => write!(f, "Client sent an unexpected packet. This should never happen! Please re-run with `log` trace-level logging enabled and file an issue at https://github.com/daniel5151/gdbstub/issues"), 135 TargetMismatch => write!(f, "Received a packet with too much data for the given target"), 136 TargetError(e) => write!(f, "Target threw a fatal error: {}", e), 137 UnsupportedStopReason => write!(f, "{} {}", unsupported_stop_reason!(), CONTEXT), 138 UnexpectedStepPacket => write!(f, "{} {}", unexpected_step_packet!(), CONTEXT), 139 140 ImplicitSwBreakpoints => write!(f, "Warning: The target has not opted into using implicit software breakpoints. See `Target::guard_rail_implicit_sw_breakpoints` for more information"), 141 MissingCurrentActivePidImpl => write!(f, "GDB client attempted to attach to a new process, but the target has not implemented support for `ExtendedMode::support_current_active_pid`"), 142 143 NonFatalError(_) => write!(f, "Internal non-fatal error. You should never see this! Please file an issue if you do!"), 144 } 145 } 146 } 147 148 #[cfg(feature = "std")] 149 impl<T, C> std::error::Error for GdbStubError<T, C> 150 where 151 C: Debug + Display, 152 T: Debug + Display, 153 { 154 } 155 156 impl<T, C> GdbStubError<T, C> { 157 /// Check if the error was due to a target error. is_target_error(&self) -> bool158 pub fn is_target_error(&self) -> bool { 159 matches!(self.kind, InternalError::TargetError(..)) 160 } 161 162 /// If the error was due to a target error, return the concrete error type. into_target_error(self) -> Option<T>163 pub fn into_target_error(self) -> Option<T> { 164 match self.kind { 165 InternalError::TargetError(e) => Some(e), 166 _ => None, 167 } 168 } 169 170 /// Check if the error was due to a connection error. is_connection_error(&self) -> bool171 pub fn is_connection_error(&self) -> bool { 172 matches!(self.kind, InternalError::Connection(..)) 173 } 174 175 /// If the error was due to a connection error, return the concrete error 176 /// type. into_connection_error(self) -> Option<(C, ConnectionErrorKind)>177 pub fn into_connection_error(self) -> Option<(C, ConnectionErrorKind)> { 178 match self.kind { 179 InternalError::Connection(e, kind) => Some((e, kind)), 180 _ => None, 181 } 182 } 183 } 184 185 impl<T, C> From<InternalError<T, C>> for GdbStubError<T, C> { from(kind: InternalError<T, C>) -> Self186 fn from(kind: InternalError<T, C>) -> Self { 187 GdbStubError { kind } 188 } 189 } 190 191 impl<A, T, C> From<CapacityError<A>> for GdbStubError<T, C> { from(_: CapacityError<A>) -> Self192 fn from(_: CapacityError<A>) -> Self { 193 InternalError::PacketBufferOverflow.into() 194 } 195 } 196