use gdbstub::common::Signal;
use gdbstub::conn::ConnectionExt;
use gdbstub::stub::{
BaseStopReason, DisconnectReason, GdbStub, SingleThreadStopReason, run_blocking,
};
use super::x86_64_target::HyperlightSandboxTarget;
use super::{DebugResponse, GdbTargetError, VcpuStopReason};
#[cfg(target_os = "linux")]
mod signals {
pub use libc::{SIGINT, SIGSEGV};
}
#[cfg(windows)]
mod signals {
pub const SIGINT: i8 = 2;
pub const SIGSEGV: i8 = 11;
}
struct GdbBlockingEventLoop;
impl run_blocking::BlockingEventLoop for GdbBlockingEventLoop {
type Connection = Box<dyn ConnectionExt<Error = std::io::Error>>;
type StopReason = SingleThreadStopReason<u64>;
type Target = HyperlightSandboxTarget;
fn wait_for_stop_reason(
target: &mut Self::Target,
conn: &mut Self::Connection,
) -> Result<
run_blocking::Event<Self::StopReason>,
run_blocking::WaitForStopReasonError<
<Self::Target as gdbstub::target::Target>::Error,
<Self::Connection as gdbstub::conn::Connection>::Error,
>,
> {
loop {
match target.try_recv() {
Ok(DebugResponse::VcpuStopped(stop_reason)) => {
tracing::debug!("VcpuStopped with reason {:?}", stop_reason);
let stop_response = match stop_reason {
VcpuStopReason::DoneStep => BaseStopReason::DoneStep,
VcpuStopReason::EntryPointBp => BaseStopReason::HwBreak(()),
VcpuStopReason::SwBp => BaseStopReason::SwBreak(()),
VcpuStopReason::HwBp => BaseStopReason::HwBreak(()),
VcpuStopReason::Interrupt => BaseStopReason::SignalWithThread {
tid: (),
signal: Signal(signals::SIGINT as u8),
},
VcpuStopReason::Crash => BaseStopReason::SignalWithThread {
tid: (),
signal: Signal(signals::SIGSEGV as u8),
},
VcpuStopReason::Unknown => {
tracing::warn!("Unknown stop reason received");
BaseStopReason::SwBreak(())
}
};
return Ok(run_blocking::Event::TargetStopped(stop_response));
}
Ok(msg) => {
tracing::error!("Unexpected message received {:?}", msg);
}
Err(crossbeam_channel::TryRecvError::Empty) => (),
Err(crossbeam_channel::TryRecvError::Disconnected) => {
return Ok(run_blocking::Event::TargetStopped(BaseStopReason::Exited(
0,
)));
}
}
if conn.peek().map(|b| b.is_some()).unwrap_or(false) {
let byte = conn
.read()
.map_err(run_blocking::WaitForStopReasonError::Connection)?;
return Ok(run_blocking::Event::IncomingData(byte));
}
}
}
fn on_interrupt(
target: &mut Self::Target,
) -> Result<Option<Self::StopReason>, <Self::Target as gdbstub::target::Target>::Error> {
tracing::info!("Received interrupt from GDB client - sending signal to target thread");
let res = target.interrupt_vcpu();
if !res {
tracing::error!("Failed to send signal to target thread");
return Err(GdbTargetError::SendSignalError);
}
Ok(None)
}
}
pub(crate) fn event_loop_thread(
debugger: GdbStub<HyperlightSandboxTarget, Box<dyn ConnectionExt<Error = std::io::Error>>>,
target: &mut HyperlightSandboxTarget,
) {
match debugger.run_blocking::<GdbBlockingEventLoop>(target) {
Ok(disconnect_reason) => match disconnect_reason {
DisconnectReason::Disconnect => {
tracing::info!("Gdb client disconnected");
if let Err(e) = target.disable_debug() {
tracing::error!("Cannot disable debugging: {:?}", e);
}
}
DisconnectReason::TargetExited(_) => {
tracing::info!("Guest finalized execution and disconnected");
}
DisconnectReason::TargetTerminated(sig) => {
tracing::info!("Gdb target terminated with signal {}", sig)
}
DisconnectReason::Kill => tracing::info!("Gdb sent a kill command"),
},
Err(e) => {
tracing::error!("fatal error encountered: {e:?}");
}
}
}