mod gdb;
use core::{marker::PhantomData, num::NonZeroI32};
use gdbstub::{
common::Signal,
conn::ConnectionExt,
stub::{
run_blocking::{self, BlockingEventLoop},
SingleThreadStopReason,
},
};
use super::{memory::Memory, Error, Interpreter, State, SYSCALL_ARGS};
#[derive(Debug, PartialEq)]
enum ExecMode {
Step,
Run,
}
#[derive(Debug)]
pub struct Debugger<
'a,
M: Memory,
C: ConnectionExt,
F: FnMut(i32, &[i32; SYSCALL_ARGS], &mut M) -> Result<Result<i32, NonZeroI32>, Error>,
const N: usize = 4,
> {
interpreter: Interpreter<'a, M>,
breakpoints: [Option<u32>; N],
exec_mode: ExecMode,
syscall_fn: F,
_conn: PhantomData<C>,
}
impl<
'a,
M: Memory,
C: ConnectionExt,
F: FnMut(i32, &[i32; SYSCALL_ARGS], &mut M) -> Result<Result<i32, NonZeroI32>, Error>,
const N: usize,
> From<Debugger<'a, M, C, F, N>> for Interpreter<'a, M>
{
fn from(debugger: Debugger<'a, M, C, F, N>) -> Self {
debugger.interpreter
}
}
impl<
'a,
M: Memory,
C: ConnectionExt,
F: FnMut(i32, &[i32; SYSCALL_ARGS], &mut M) -> Result<Result<i32, NonZeroI32>, Error>,
const N: usize,
> Debugger<'a, M, C, F, N>
{
pub fn new(memory: &'a mut M, syscall_fn: F) -> Self {
Self {
interpreter: Interpreter::new(memory, 0),
breakpoints: [None; N],
exec_mode: ExecMode::Run,
syscall_fn,
_conn: PhantomData,
}
}
}
impl<
M: Memory,
C: ConnectionExt,
F: FnMut(i32, &[i32; SYSCALL_ARGS], &mut M) -> Result<Result<i32, NonZeroI32>, Error>,
const N: usize,
> BlockingEventLoop for Debugger<'_, M, C, F, N>
{
type Target = Self;
type Connection = C;
type StopReason = SingleThreadStopReason<u32>;
fn wait_for_stop_reason(
target: &mut Self::Target,
conn: &mut Self::Connection,
) -> Result<
gdbstub::stub::run_blocking::Event<Self::StopReason>,
gdbstub::stub::run_blocking::WaitForStopReasonError<
<Self::Target as gdbstub::target::Target>::Error,
<Self::Connection as gdbstub::conn::Connection>::Error,
>,
> {
let mut cycles = 0;
loop {
match target
.interpreter
.step()
.map_err(run_blocking::WaitForStopReasonError::Target)?
{
State::Running => (),
State::Halted => {
return Ok(run_blocking::Event::TargetStopped(
SingleThreadStopReason::Terminated(Signal::SIGSTOP),
))
}
State::Called => target
.interpreter
.syscall(&mut target.syscall_fn)
.map_err(run_blocking::WaitForStopReasonError::Target)?,
State::Waiting => target
.interpreter
.interrupt(0)
.map_err(run_blocking::WaitForStopReasonError::Target)?,
}
if target
.breakpoints
.contains(&Some(target.interpreter.program_counter))
{
return Ok(run_blocking::Event::TargetStopped(
SingleThreadStopReason::SwBreak(()),
));
}
if target.exec_mode == ExecMode::Step {
return Ok(run_blocking::Event::TargetStopped(
SingleThreadStopReason::DoneStep,
));
}
if cycles % 1024 == 0 && conn.peek().map(|b| b.is_some()).unwrap_or(true) {
let byte = conn
.read()
.map_err(run_blocking::WaitForStopReasonError::Connection)?;
return Ok(run_blocking::Event::IncomingData(byte));
}
cycles += 1;
}
}
fn on_interrupt(
_target: &mut Self::Target,
) -> Result<Option<Self::StopReason>, <Self::Target as gdbstub::target::Target>::Error> {
Ok(Some(SingleThreadStopReason::Signal(Signal::SIGINT)))
}
}