• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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