aboutsummaryrefslogtreecommitdiff
path: root/src/stub/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stub/error.rs')
-rw-r--r--src/stub/error.rs244
1 files changed, 155 insertions, 89 deletions
diff --git a/src/stub/error.rs b/src/stub/error.rs
index 6d55997..8cdc18e 100644
--- a/src/stub/error.rs
+++ b/src/stub/error.rs
@@ -1,62 +1,42 @@
-use core::fmt::{self, Debug, Display};
-
-use crate::arch::SingleStepGdbBehavior;
-use crate::protocol::{PacketParseError, ResponseWriterError};
+use crate::protocol::PacketParseError;
+use crate::protocol::ResponseWriterError;
use crate::util::managed_vec::CapacityError;
+use core::fmt::Debug;
+use core::fmt::Display;
+use core::fmt::{self};
+
+/// An error that may occur while interacting with a
+/// [`Connection`](crate::conn::Connection).
+#[derive(Debug)]
+pub enum ConnectionErrorKind {
+ /// Error initializing the session.
+ Init,
+ /// Error reading data.
+ Read,
+ /// Error writing data.
+ Write,
+}
-/// An error which may occur during a GDB debugging session.
#[derive(Debug)]
-#[non_exhaustive]
-pub enum GdbStubError<T, C> {
- /// Connection Error while initializing the session.
- ConnectionInit(C),
- /// Connection Error while reading request.
- ConnectionRead(C),
- /// Connection Error while writing response.
- ConnectionWrite(C),
-
- /// Client nack'd the last packet, but `gdbstub` doesn't implement
- /// re-transmission.
+pub(crate) enum InternalError<T, C> {
+ /// Connection Error
+ Connection(C, ConnectionErrorKind),
+ /// Target encountered a fatal error.
+ TargetError(T),
+
ClientSentNack,
- /// Packet cannot fit in the provided packet buffer.
PacketBufferOverflow,
- /// Could not parse the packet into a valid command.
PacketParse(PacketParseError),
- /// GDB 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>
PacketUnexpected,
- /// GDB client sent a packet with too much data for the given target.
TargetMismatch,
- /// Target encountered a fatal error.
- TargetError(T),
- /// Target responded with an unsupported stop reason.
- ///
- /// Certain stop reasons can only be used when their associated protocol
- /// feature has been implemented. e.g: a Target cannot return a
- /// `StopReason::HwBreak` if the hardware breakpoints IDET hasn't been
- /// implemented.
UnsupportedStopReason,
- /// Target didn't report any active threads when there should have been at
- /// least one running.
- NoActiveThreads,
-
- /// The target has not opted into using implicit software breakpoints.
- /// See [`Target::guard_rail_implicit_sw_breakpoints`] for more information.
- ///
- /// [`Target::guard_rail_implicit_sw_breakpoints`]:
- /// crate::target::Target::guard_rail_implicit_sw_breakpoints
+ UnexpectedStepPacket,
ImplicitSwBreakpoints,
- /// The target has not indicated support for optional single stepping. See
- /// [`Target::guard_rail_single_step_gdb_behavior`] for more information.
- ///
- /// If you encountered this error while using an `Arch` implementation
- /// defined in `gdbstub_arch` and believe this is incorrect, please file an
- /// issue at <https://github.com/daniel5151/gdbstub/issues>.
- ///
- /// [`Target::guard_rail_single_step_gdb_behavior`]:
- /// crate::target::Target::guard_rail_single_step_gdb_behavior
- SingleStepGdbBehavior(SingleStepGdbBehavior),
+ // DEVNOTE: this is a temporary workaround for something that can and should
+ // be caught at compile time via IDETs. That said, since i'm not sure when
+ // I'll find the time to cut a breaking release of gdbstub, I'd prefer to
+ // push out this feature as a non-breaking change now.
+ MissingCurrentActivePidImpl,
// Internal - A non-fatal error occurred (with errno-style error code)
//
@@ -67,55 +47,100 @@ pub enum GdbStubError<T, C> {
NonFatalError(u8),
}
-impl<T, C> From<ResponseWriterError<C>> for GdbStubError<T, C> {
- fn from(e: ResponseWriterError<C>) -> Self {
- GdbStubError::ConnectionWrite(e.0)
+impl<T, C> InternalError<T, C> {
+ pub fn conn_read(e: C) -> Self {
+ InternalError::Connection(e, ConnectionErrorKind::Read)
+ }
+
+ pub fn conn_write(e: C) -> Self {
+ InternalError::Connection(e, ConnectionErrorKind::Write)
+ }
+
+ pub fn conn_init(e: C) -> Self {
+ InternalError::Connection(e, ConnectionErrorKind::Init)
}
}
-impl<A, T, C> From<CapacityError<A>> for GdbStubError<T, C> {
- fn from(_: CapacityError<A>) -> Self {
- GdbStubError::PacketBufferOverflow
+impl<T, C> From<ResponseWriterError<C>> for InternalError<T, C> {
+ fn from(e: ResponseWriterError<C>) -> Self {
+ InternalError::Connection(e.0, ConnectionErrorKind::Write)
}
}
+// these macros are used to keep the docs and `Display` impl in-sync
+
+macro_rules! unsupported_stop_reason {
+ () => {
+ "User error: cannot report stop reason without also implementing its corresponding IDET"
+ };
+}
+
+macro_rules! unexpected_step_packet {
+ () => {
+ "Received an unexpected `step` request. This is most-likely due to this GDB client bug: <https://sourceware.org/bugzilla/show_bug.cgi?id=28440>"
+ };
+}
+
+/// An error which may occur during a GDB debugging session.
+///
+/// ## Additional Notes
+///
+/// `GdbStubError`'s inherent `Display` impl typically contains enough context
+/// for users to understand why the error occurred.
+///
+/// That said, there are a few instances where the error condition requires
+/// additional context.
+///
+/// * * *
+#[doc = concat!("_", unsupported_stop_reason!(), "_")]
+///
+/// This is a not a bug with `gdbstub`. Rather, this is indicative of a bug in
+/// your `gdbstub` integration.
+///
+/// Certain stop reasons can only be used when their associated protocol feature
+/// has been implemented. e.g: a Target cannot return a `StopReason::HwBreak` if
+/// the hardware breakpoints IDET hasn't been implemented.
+///
+/// Please double-check that you've implemented all the necessary `supports_`
+/// methods related to the stop reason you're trying to report.
+///
+/// * * *
+#[doc = concat!("_", unexpected_step_packet!(), "_")]
+///
+/// Unfortunately, there's nothing `gdbstub` can do to work around this bug.
+///
+/// Until the issue is fixed upstream, certain architectures are essentially
+/// forced to manually implement single-step support.
+#[derive(Debug)]
+pub struct GdbStubError<T, C> {
+ kind: InternalError<T, C>,
+}
+
impl<T, C> Display for GdbStubError<T, C>
where
- C: Debug,
- T: Debug,
+ C: Display,
+ T: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use self::GdbStubError::*;
- match self {
- ConnectionInit(e) => write!(f, "Connection Error while initializing the session: {:?}", e),
- ConnectionRead(e) => write!(f, "Connection Error while reading request: {:?}", e),
- ConnectionWrite(e) => write!(f, "Connection Error while writing response: {:?}", e),
+ use self::InternalError::*;
+ const CONTEXT: &str = "See the `GdbStubError` docs for more details";
+ match &self.kind {
+ Connection(e, ConnectionErrorKind::Init) => write!(f, "Connection Error while initializing the session: {}", e),
+ Connection(e, ConnectionErrorKind::Read) => write!(f, "Connection Error while reading request: {}", e),
+ Connection(e, ConnectionErrorKind::Write) => write!(f, "Connection Error while writing response: {}", e),
ClientSentNack => write!(f, "Client nack'd the last packet, but `gdbstub` doesn't implement re-transmission."),
- PacketBufferOverflow => write!(f, "Packet too big for provided buffer!"),
- PacketParse(e) => write!(f, "Could not parse the packet into a valid command: {:?}", e),
- PacketUnexpected => write!(f, "Client sent an unexpected packet. Please re-run with `log` trace-level logging enabled and file an issue at https://github.com/daniel5151/gdbstub/issues"),
- TargetMismatch => write!(f, "GDB client sent a packet with too much data for the given target."),
- TargetError(e) => write!(f, "Target threw a fatal error: {:?}", e),
- UnsupportedStopReason => write!(f, "Target responded with an unsupported stop reason."),
- NoActiveThreads => write!(f, "Target didn't report any active threads when there should have been at least one running."),
-
- ImplicitSwBreakpoints => write!(f, "Warning: The target has not opted into using implicit software breakpoints. See `Target::guard_rail_implicit_sw_breakpoints` for more information."),
- SingleStepGdbBehavior(behavior) => {
- use crate::arch::SingleStepGdbBehavior;
- write!(
- f,
- "Warning: Mismatch between the targets' single-step support and arch-level single-step behavior: {} ",
- match behavior {
- SingleStepGdbBehavior::Optional => "", // unreachable, since optional single step will not result in an error
- SingleStepGdbBehavior::Required => "GDB requires single-step support on this arch.",
- SingleStepGdbBehavior::Ignored => "GDB ignores single-step support on this arch, yet the target has implemented support for it.",
- SingleStepGdbBehavior::Unknown => "This arch's single-step behavior hasn't been tested yet: please conduct a test + upstream your findings!",
- }
- )?;
- write!(f, "See `Target::guard_rail_single_step_gdb_behavior` for more information.")
- },
-
- NonFatalError(_) => write!(f, "Internal non-fatal error. End users should never see this! Please file an issue if you do!"),
+ PacketBufferOverflow => write!(f, "Received an oversized packet (did not fit in provided packet buffer)"),
+ PacketParse(e) => write!(f, "Failed to parse packet into a valid command: {:?}", e),
+ 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"),
+ TargetMismatch => write!(f, "Received a packet with too much data for the given target"),
+ TargetError(e) => write!(f, "Target threw a fatal error: {}", e),
+ UnsupportedStopReason => write!(f, "{} {}", unsupported_stop_reason!(), CONTEXT),
+ UnexpectedStepPacket => write!(f, "{} {}", unexpected_step_packet!(), CONTEXT),
+
+ ImplicitSwBreakpoints => write!(f, "Warning: The target has not opted into using implicit software breakpoints. See `Target::guard_rail_implicit_sw_breakpoints` for more information"),
+ 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`"),
+
+ NonFatalError(_) => write!(f, "Internal non-fatal error. You should never see this! Please file an issue if you do!"),
}
}
}
@@ -123,7 +148,48 @@ where
#[cfg(feature = "std")]
impl<T, C> std::error::Error for GdbStubError<T, C>
where
- C: Debug,
- T: Debug,
+ C: Debug + Display,
+ T: Debug + Display,
{
}
+
+impl<T, C> GdbStubError<T, C> {
+ /// Check if the error was due to a target error.
+ pub fn is_target_error(&self) -> bool {
+ matches!(self.kind, InternalError::TargetError(..))
+ }
+
+ /// If the error was due to a target error, return the concrete error type.
+ pub fn into_target_error(self) -> Option<T> {
+ match self.kind {
+ InternalError::TargetError(e) => Some(e),
+ _ => None,
+ }
+ }
+
+ /// Check if the error was due to a connection error.
+ pub fn is_connection_error(&self) -> bool {
+ matches!(self.kind, InternalError::Connection(..))
+ }
+
+ /// If the error was due to a connection error, return the concrete error
+ /// type.
+ pub fn into_connection_error(self) -> Option<(C, ConnectionErrorKind)> {
+ match self.kind {
+ InternalError::Connection(e, kind) => Some((e, kind)),
+ _ => None,
+ }
+ }
+}
+
+impl<T, C> From<InternalError<T, C>> for GdbStubError<T, C> {
+ fn from(kind: InternalError<T, C>) -> Self {
+ GdbStubError { kind }
+ }
+}
+
+impl<A, T, C> From<CapacityError<A>> for GdbStubError<T, C> {
+ fn from(_: CapacityError<A>) -> Self {
+ InternalError::PacketBufferOverflow.into()
+ }
+}