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