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