use crate::protocol::PacketParseError;
use crate::protocol::ResponseWriterError;
use crate::util::managed_vec::CapacityError;
#[cfg(feature = "core_error")]
use core::error::Error as CoreError;
use core::fmt::Debug;
use core::fmt::Display;
use core::fmt::{self};
#[cfg(all(feature = "std", not(feature = "core_error")))]
use std::error::Error as CoreError;
#[derive(Debug)]
pub enum ConnectionErrorKind {
Init,
Read,
Write,
}
#[derive(Debug)]
pub(crate) enum InternalError<T, C> {
Connection(C, ConnectionErrorKind),
TargetError(T),
ClientSentNack,
PacketBufferOverflow,
PacketParse(PacketParseError),
PacketUnexpected,
TargetMismatch,
UnsupportedStopReason,
UnexpectedStepPacket,
ImplicitSwBreakpoints,
MissingCurrentActivePidImpl,
TracepointFeatureUnimplemented(u8),
TracepointUnsupportedSourceEnumeration,
MissingMultiThreadSchedulerLocking,
MissingToRawId,
#[doc(hidden)]
NonFatalError(u8),
}
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<T, C> From<ResponseWriterError<C>> for InternalError<T, C> {
fn from(e: ResponseWriterError<C>) -> Self {
InternalError::Connection(e.0, ConnectionErrorKind::Write)
}
}
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>"
};
}
#[doc = concat!("_", unsupported_stop_reason!(), "_")]
#[doc = concat!("_", unexpected_step_packet!(), "_")]
#[derive(Debug)]
pub struct GdbStubError<T, C> {
kind: InternalError<T, C>,
}
impl<T, C> Display for GdbStubError<T, C>
where
C: Display,
T: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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, "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`"),
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),
TracepointUnsupportedSourceEnumeration => write!(f, "The target doesn't support the gdbstub TracepointSource extension, but attempted to transition to enumerating tracepoint sources"),
MissingMultiThreadSchedulerLocking => write!(f, "GDB requested Scheduler Locking, but the Target does not implement the `MultiThreadSchedulerLocking` IDET"),
MissingToRawId => write!(f, "A RegId was used with an API that requires raw register IDs to be available (e.g. `report_stop_with_regs`) but returned `None` from `to_raw_id()`"),
NonFatalError(_) => write!(f, "Internal non-fatal error. You should never see this! Please file an issue if you do!"),
}
}
}
#[cfg(any(feature = "std", feature = "core_error"))]
impl<T, C> CoreError for GdbStubError<T, C>
where
C: Debug + Display,
T: Debug + Display,
{
}
impl<T, C> GdbStubError<T, C> {
pub fn is_target_error(&self) -> bool {
matches!(self.kind, InternalError::TargetError(..))
}
pub fn into_target_error(self) -> Option<T> {
match self.kind {
InternalError::TargetError(e) => Some(e),
_ => None,
}
}
pub fn is_connection_error(&self) -> bool {
matches!(self.kind, InternalError::Connection(..))
}
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()
}
}