Skip to content

Commit

Permalink
Refactors debugging-related code (#1123)
Browse files Browse the repository at this point in the history
  • Loading branch information
SuchAFuriousDeath authored Nov 20, 2024
1 parent 1005d74 commit 12c65cd
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 77 deletions.
143 changes: 80 additions & 63 deletions gui/src/vmm/debug/mod.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,96 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
use super::cpu::CpuManager;
use super::cpu::{CpuManager, GdbError};
use crate::debug::DebugClient;
use crate::error::RustError;
use crate::hv::Hypervisor;
use crate::screen::Screen;
use gdbstub::stub::state_machine::state::{Idle, Running};
use gdbstub::stub::state_machine::{GdbStubStateMachine, GdbStubStateMachineInner};
use gdbstub::stub::MultiThreadStopReason;
use thiserror::Error;

pub fn dispatch_idle<H: Hypervisor, S: Screen>(
target: &mut CpuManager<H, S>,
mut state: GdbStubStateMachineInner<
'static,
Idle<CpuManager<H, S>>,
CpuManager<H, S>,
DebugClient,
>,
) -> Result<
Result<
GdbStubStateMachine<'static, CpuManager<H, S>, DebugClient>,
GdbStubStateMachineInner<'static, Idle<CpuManager<H, S>>, CpuManager<H, S>, DebugClient>,
>,
RustError,
> {
// Check for pending command.
let b = match state.borrow_conn().read() {
Ok(v) => v,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return Ok(Err(state)),
Err(e) => {
return Err(RustError::with_source(
"couldn't read data from the debugger",
e,
));
impl<H: Hypervisor, S: Screen> CpuManager<H, S> {
pub(super) fn dispatch_gdb_idle(
&mut self,
mut state: GdbStubStateMachineInner<
'static,
Idle<CpuManager<H, S>>,
CpuManager<H, S>,
DebugClient,
>,
) -> Result<
Result<
GdbStubStateMachine<'static, CpuManager<H, S>, DebugClient>,
GdbStubStateMachineInner<
'static,
Idle<CpuManager<H, S>>,
CpuManager<H, S>,
DebugClient,
>,
>,
DispatchGdbIdleError,
> {
let b = match state.borrow_conn().read() {
Ok(v) => v,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return Ok(Err(state)),
Err(e) => return Err(DispatchGdbIdleError::ReadData(e)),
};

state
.incoming_data(self, b)
.map(Ok)
.map_err(DispatchGdbIdleError::ProcessData)
}

pub(super) fn dispatch_gdb_running(
&mut self,
mut state: GdbStubStateMachineInner<'static, Running, CpuManager<H, S>, DebugClient>,
stop: Option<MultiThreadStopReason<u64>>,
) -> Result<
Result<
GdbStubStateMachine<'static, CpuManager<H, S>, DebugClient>,
GdbStubStateMachineInner<'static, Running, CpuManager<H, S>, DebugClient>,
>,
DispatchGdbRunningError,
> {
// Check If we are here because of a breakpoint.
if let Some(r) = stop {
return state
.report_stop(self, r)
.map(Ok)
.map_err(DispatchGdbRunningError::ReportStopReason);
}
};

state
.incoming_data(target, b)
.map(Ok)
.map_err(|e| RustError::with_source("couldn't process data from the debugger", e))
}
// Check for pending command.
let b = match state.borrow_conn().read() {
Ok(v) => v,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return Ok(Err(state)),
Err(e) => return Err(DispatchGdbRunningError::ReadData(e)),
};

pub fn dispatch_running<H: Hypervisor, S: Screen>(
target: &mut CpuManager<H, S>,
mut state: GdbStubStateMachineInner<'static, Running, CpuManager<H, S>, DebugClient>,
stop: Option<MultiThreadStopReason<u64>>,
) -> Result<
Result<
GdbStubStateMachine<'static, CpuManager<H, S>, DebugClient>,
GdbStubStateMachineInner<'static, Running, CpuManager<H, S>, DebugClient>,
>,
RustError,
> {
// Check If we are here because of a breakpoint.
if let Some(r) = stop {
return state
.report_stop(target, r)
state
.incoming_data(self, b)
.map(Ok)
.map_err(|e| RustError::with_source("couldn't report stop reason to the debugger", e));
.map_err(DispatchGdbRunningError::ProcessData)
}
}

// Check for pending command.
let b = match state.borrow_conn().read() {
Ok(v) => v,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => return Ok(Err(state)),
Err(e) => {
return Err(RustError::with_source(
"couldn't read data from the debugger",
e,
));
}
};
#[derive(Debug, Error)]
pub(super) enum DispatchGdbIdleError {
#[error("couldn't read data from the debugger")]
ReadData(#[source] std::io::Error),

#[error("couldn't process data from the debugger")]
ProcessData(#[source] gdbstub::stub::GdbStubError<GdbError, std::io::Error>),
}

#[derive(Debug, Error)]
pub(super) enum DispatchGdbRunningError {
#[error("couldn't report stop reason to the debugger")]
ReportStopReason(#[source] gdbstub::stub::GdbStubError<GdbError, std::io::Error>),

#[error("couldn't read data from the debugger")]
ReadData(#[source] std::io::Error),

state
.incoming_data(target, b)
.map(Ok)
.map_err(|e| RustError::with_source("couldn't process data from the debugger", e))
#[error("couldn't process data from the debugger")]
ProcessData(#[source] gdbstub::stub::GdbStubError<GdbError, std::io::Error>),
}
11 changes: 9 additions & 2 deletions gui/src/vmm/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{DebugResult, KernelStop, Vmm, VmmEvent, VmmScreen};
use super::{DebugResult, DispatchDebugResult, KernelStop, Vmm, VmmEvent, VmmScreen};
use crate::debug::DebugClient;
use crate::error::RustError;
use crate::profile::Profile;
Expand Down Expand Up @@ -82,7 +82,14 @@ pub unsafe extern "C" fn vmm_dispatch_debug(vmm: *mut Vmm, stop: *mut KernelStop
Some(Box::from_raw(stop).0)
};

vmm.dispatch_debug(stop)
match vmm.dispatch_debug(stop) {
Ok(DispatchDebugResult::Ok) => DebugResult::Ok,
Ok(DispatchDebugResult::Disconnected) => DebugResult::Disconnected,
Err(e) => {
let e = RustError::wrap(e).into_c();
DebugResult::Error { reason: e }
}
}
}

#[no_mangle]
Expand Down
44 changes: 32 additions & 12 deletions gui/src/vmm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,30 +347,33 @@ impl Vmm {
Ok(vmm)
}

fn dispatch_debug(&mut self, mut stop: Option<MultiThreadStopReason<u64>>) -> DebugResult {
fn dispatch_debug(
&mut self,
mut stop: Option<MultiThreadStopReason<u64>>,
) -> Result<DispatchDebugResult, DispatchDebugError> {
loop {
// Check current state.
let r = match self.gdb.take().unwrap() {
GdbStubStateMachine::Idle(s) => {
match debug::dispatch_idle(&mut self.cpu, s) {
match self.cpu.dispatch_gdb_idle(s) {
Ok(Ok(v)) => Ok(v),
Ok(Err(v)) => {
// No pending data from the debugger.
self.gdb = Some(v.into());
return DebugResult::Ok;
return Ok(DispatchDebugResult::Ok);
}
Err(e) => Err(e),
Err(e) => Err(DispatchDebugError::DispatchIdle(e)),
}
}
GdbStubStateMachine::Running(s) => {
match debug::dispatch_running(&mut self.cpu, s, stop.take()) {
match self.cpu.dispatch_gdb_running(s, stop.take()) {
Ok(Ok(v)) => Ok(v),
Ok(Err(v)) => {
// No pending data from the debugger.
self.gdb = Some(v.into());
return DebugResult::Ok;
return Ok(DispatchDebugResult::Ok);
}
Err(e) => Err(e),
Err(e) => Err(DispatchDebugError::DispatchRunning(e)),
}
}
GdbStubStateMachine::CtrlCInterrupt(s) => {
Expand All @@ -380,16 +383,16 @@ impl Vmm {
&mut self.cpu,
Some(MultiThreadStopReason::Signal(Signal::SIGINT)),
)
.map_err(|e| {
RustError::with_source("couldn't handle CTRL+C from a debugger", e)
})
.map_err(DispatchDebugError::HandleInterrupt)
}
GdbStubStateMachine::Disconnected(_) => {
return Ok(DispatchDebugResult::Disconnected)
}
GdbStubStateMachine::Disconnected(_) => return DebugResult::Disconnected,
};

match r {
Ok(v) => self.gdb = Some(v),
Err(e) => return DebugResult::Error { reason: e.into_c() },
Err(e) => return Err(e),
}
}
}
Expand All @@ -403,6 +406,23 @@ impl Drop for Vmm {
}
}

pub enum DispatchDebugResult {
Ok,
Disconnected,
}

#[derive(Debug, Error)]
enum DispatchDebugError {
#[error("couldn't dispatch idle state")]
DispatchIdle(#[source] debug::DispatchGdbIdleError),

#[error("couldn't dispatch running state")]
DispatchRunning(#[source] debug::DispatchGdbRunningError),

#[error("couldn't handle CTRL+C interrupt")]
HandleInterrupt(#[source] gdbstub::stub::GdbStubError<GdbError, std::io::Error>),
}

/// Contains objects required to render the screen.
#[repr(C)]
pub struct VmmScreen {
Expand Down

0 comments on commit 12c65cd

Please sign in to comment.