Skip to content

Commit

Permalink
introduce GdbInterrupt wrapper type
Browse files Browse the repository at this point in the history
might as well make this breaking API change now, making it less painful
to introduce a non-blocking interrupt type down the line.
  • Loading branch information
daniel5151 committed May 8, 2021
1 parent 6bc47ea commit 9e0526d
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 35 deletions.
6 changes: 4 additions & 2 deletions example_no_std/src/gdb.rs
@@ -1,6 +1,8 @@
use gdbstub::common::Tid;
use gdbstub::target;
use gdbstub::target::ext::base::multithread::{MultiThreadOps, ResumeAction, ThreadStopReason};
use gdbstub::target::ext::base::multithread::{
GdbInterrupt, MultiThreadOps, ResumeAction, ThreadStopReason,
};
use gdbstub::target::{Target, TargetResult};

use crate::print_str::print_str;
Expand Down Expand Up @@ -37,7 +39,7 @@ impl MultiThreadOps for DummyTarget {
fn resume(
&mut self,
_default_resume_action: ResumeAction,
_check_gdb_interrupt: &mut dyn FnMut() -> bool,
_check_gdb_interrupt: GdbInterrupt<'_>,
) -> Result<ThreadStopReason<u32>, Self::Error> {
print_str("> resume");
Ok(ThreadStopReason::DoneStep)
Expand Down
36 changes: 24 additions & 12 deletions examples/armv4t/gdb/mod.rs
Expand Up @@ -3,8 +3,8 @@ use core::convert::TryInto;
use armv4t_emu::{reg, Memory};
use gdbstub::target;
use gdbstub::target::ext::base::singlethread::{
ResumeAction, SingleThreadOps, SingleThreadReverseContOps, SingleThreadReverseStepOps,
StopReason,
GdbInterrupt, ResumeAction, SingleThreadOps, SingleThreadReverseContOps,
SingleThreadReverseStepOps, StopReason,
};
use gdbstub::target::ext::breakpoints::WatchKind;
use gdbstub::target::{Target, TargetError, TargetResult};
Expand Down Expand Up @@ -75,12 +75,12 @@ impl Target for Emu {
}
}

impl SingleThreadOps for Emu {
fn resume(
impl Emu {
fn inner_resume(
&mut self,
action: ResumeAction,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
) -> Result<StopReason<u32>, Self::Error> {
mut check_gdb_interrupt: impl FnMut() -> bool,
) -> Result<StopReason<u32>, &'static str> {
let event = match action {
ResumeAction::Step => match self.step() {
Some(e) => e,
Expand Down Expand Up @@ -116,6 +116,17 @@ impl SingleThreadOps for Emu {
},
})
}
}

impl SingleThreadOps for Emu {
fn resume(
&mut self,
action: ResumeAction,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<u32>, Self::Error> {
let mut gdb_interrupt = gdb_interrupt.no_async();
self.inner_resume(action, || gdb_interrupt.pending())
}

fn read_registers(
&mut self,
Expand Down Expand Up @@ -228,26 +239,26 @@ impl target::ext::base::SingleRegisterAccess<()> for Emu {
impl target::ext::base::singlethread::SingleThreadReverseCont for Emu {
fn reverse_cont(
&mut self,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<u32>, Self::Error> {
// FIXME: actually implement reverse step
eprintln!(
"FIXME: Not actually reverse-continuing. Performing forwards continue instead..."
);
self.resume(ResumeAction::Continue, check_gdb_interrupt)
self.resume(ResumeAction::Continue, gdb_interrupt)
}
}

impl target::ext::base::singlethread::SingleThreadReverseStep for Emu {
fn reverse_step(
&mut self,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<u32>, Self::Error> {
// FIXME: actually implement reverse step
eprintln!(
"FIXME: Not actually reverse-stepping. Performing single forwards step instead..."
);
self.resume(ResumeAction::Step, check_gdb_interrupt)
self.resume(ResumeAction::Step, gdb_interrupt)
}
}

Expand All @@ -256,10 +267,11 @@ impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
&mut self,
start: u32,
end: u32,
check_gdb_interrupt: &mut dyn std::ops::FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<u32>, Self::Error> {
let mut gdb_interrupt = gdb_interrupt.no_async();
loop {
match self.resume(ResumeAction::Step, check_gdb_interrupt)? {
match self.inner_resume(ResumeAction::Step, || gdb_interrupt.pending())? {
StopReason::DoneStep => {}
stop_reason => return Ok(stop_reason),
}
Expand Down
9 changes: 6 additions & 3 deletions examples/armv4t_multicore/gdb.rs
Expand Up @@ -2,7 +2,9 @@ use armv4t_emu::{reg, Memory};

use gdbstub::common::Tid;
use gdbstub::target;
use gdbstub::target::ext::base::multithread::{MultiThreadOps, ResumeAction, ThreadStopReason};
use gdbstub::target::ext::base::multithread::{
GdbInterrupt, MultiThreadOps, ResumeAction, ThreadStopReason,
};
use gdbstub::target::ext::breakpoints::WatchKind;
use gdbstub::target::{Target, TargetError, TargetResult};

Expand Down Expand Up @@ -60,7 +62,7 @@ impl MultiThreadOps for Emu {
fn resume(
&mut self,
default_resume_action: ResumeAction,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<ThreadStopReason<u32>, Self::Error> {
// In general, the behavior of multi-threaded systems during debugging is
// determined by the system scheduler. On certain systems, this behavior can be
Expand All @@ -82,10 +84,11 @@ impl MultiThreadOps for Emu {
None => Ok(ThreadStopReason::DoneStep),
},
false => {
let mut gdb_interrupt = gdb_interrupt.no_async();
let mut cycles: usize = 0;
loop {
// check for GDB interrupt every 1024 instructions
if cycles % 1024 == 0 && check_gdb_interrupt() {
if cycles % 1024 == 0 && gdb_interrupt.pending() {
return Ok(ThreadStopReason::GdbInterrupt);
}
cycles += 1;
Expand Down
11 changes: 7 additions & 4 deletions src/gdbstub_impl/ext/base.rs
Expand Up @@ -4,7 +4,7 @@ use crate::protocol::commands::ext::Base;
use crate::arch::{Arch, Registers};
use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId};
use crate::target::ext::base::multithread::ThreadStopReason;
use crate::target::ext::base::{BaseOps, ReplayLogPosition, ResumeAction};
use crate::target::ext::base::{BaseOps, GdbInterrupt, ReplayLogPosition, ResumeAction};
use crate::{FAKE_PID, SINGLE_THREAD_TID};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Expand Down Expand Up @@ -479,7 +479,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
let end = end.decode().map_err(|_| Error::TargetMismatch)?;

let ret = ops
.resume_range_step(start, end, &mut check_gdb_interrupt)
.resume_range_step(start, end, GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?
.into();
err?;
Expand All @@ -493,7 +493,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
};

let ret = ops
.resume(action, &mut check_gdb_interrupt)
.resume(action, GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?
.into();
err?;
Expand Down Expand Up @@ -573,7 +573,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
};

let ret = ops
.resume(default_resume_action, &mut check_gdb_interrupt)
.resume(
default_resume_action,
GdbInterrupt::new(&mut check_gdb_interrupt),
)
.map_err(Error::TargetError)?;

err?;
Expand Down
10 changes: 5 additions & 5 deletions src/gdbstub_impl/ext/reverse_exec.rs
Expand Up @@ -5,7 +5,7 @@ use crate::arch::Arch;
use crate::protocol::SpecificIdKind;
use crate::target::ext::base::multithread::{MultiThreadReverseCont, MultiThreadReverseStep};
use crate::target::ext::base::singlethread::{SingleThreadReverseCont, SingleThreadReverseStep};
use crate::target::ext::base::BaseOps;
use crate::target::ext::base::{BaseOps, GdbInterrupt};

enum ReverseContOps<'a, A: Arch, E> {
SingleThread(&'a mut dyn SingleThreadReverseCont<Arch = A, Error = E>),
Expand Down Expand Up @@ -55,10 +55,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {

let stop_reason = match ops {
ReverseContOps::MultiThread(ops) => ops
.reverse_cont(&mut check_gdb_interrupt)
.reverse_cont(GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?,
ReverseContOps::SingleThread(ops) => ops
.reverse_cont(&mut check_gdb_interrupt)
.reverse_cont(GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?
.into(),
};
Expand Down Expand Up @@ -118,10 +118,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {

let stop_reason = match ops {
ReverseStepOps::MultiThread(ops) => ops
.reverse_step(tid, &mut check_gdb_interrupt)
.reverse_step(tid, GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?,
ReverseStepOps::SingleThread(ops) => ops
.reverse_step(&mut check_gdb_interrupt)
.reverse_step(GdbInterrupt::new(&mut check_gdb_interrupt))
.map_err(Error::TargetError)?
.into(),
};
Expand Down
53 changes: 53 additions & 0 deletions src/target/ext/base/mod.rs
Expand Up @@ -51,3 +51,56 @@ pub enum ReplayLogPosition {
/// Reached the end of the replay log.
End,
}

/// A handle to check for incoming GDB interrupts.
///
/// At the moment, checking for incoming interrupts requires periodically
/// polling for pending interrupts. e.g:
///
/// ```ignore
/// let interrupts = gdb_interrupt.no_async();
/// loop {
/// if interrupts.pending() {
/// return Ok(StopReason::GdbInterrupt)
/// }
///
/// // execute some number of clock cycles
/// for _ in 0..1024 {
/// match self.system.step() { .. }
/// }
/// }
/// ```
///
/// There is an outstanding issue to add a non-blocking interface to
/// `GdbInterrupt` (see [daniel5151/gdbstub#36](https://github.com/daniel5151/gdbstub/issues/36)).
/// Please comment on the issue if this is something you'd like to see
/// implemented and/or would like to help out with!
pub struct GdbInterrupt<'a> {
inner: &'a mut dyn FnMut() -> bool,
}

impl<'a> GdbInterrupt<'a> {
pub(crate) fn new(inner: &'a mut dyn FnMut() -> bool) -> GdbInterrupt<'a> {
GdbInterrupt { inner }
}

/// Returns a [`GdbInterruptNoAsync`] struct which can be polled using a
/// simple non-blocking [`pending(&mut self) ->
/// bool`](GdbInterruptNoAsync::pending) method.
pub fn no_async(self) -> GdbInterruptNoAsync<'a> {
GdbInterruptNoAsync { inner: self.inner }
}
}

/// A simplified interface to [`GdbInterrupt`] for projects without
/// async/await infrastructure.
pub struct GdbInterruptNoAsync<'a> {
inner: &'a mut dyn FnMut() -> bool,
}

impl<'a> GdbInterruptNoAsync<'a> {
/// Checks if there is a pending GDB interrupt.
pub fn pending(&mut self) -> bool {
(self.inner)()
}
}
8 changes: 4 additions & 4 deletions src/target/ext/base/multithread.rs
Expand Up @@ -8,7 +8,7 @@ use crate::target::{Target, TargetResult};
use super::{ReplayLogPosition, SingleRegisterAccessOps};

// Convenient re-exports
pub use super::ResumeAction;
pub use super::{GdbInterrupt, ResumeAction};

/// Base debugging operations for multi threaded targets.
#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -70,7 +70,7 @@ pub trait MultiThreadOps: Target {
fn resume(
&mut self,
default_resume_action: ResumeAction,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;

/// Clear all previously set resume actions.
Expand Down Expand Up @@ -214,7 +214,7 @@ pub trait MultiThreadReverseCont: Target + MultiThreadOps {
/// Reverse-continue the target.
fn reverse_cont(
&mut self,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
}

Expand All @@ -230,7 +230,7 @@ pub trait MultiThreadReverseStep: Target + MultiThreadOps {
fn reverse_step(
&mut self,
tid: Tid,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<ThreadStopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
}

Expand Down
10 changes: 5 additions & 5 deletions src/target/ext/base/singlethread.rs
Expand Up @@ -7,7 +7,7 @@ use crate::target::{Target, TargetResult};
use super::{ReplayLogPosition, SingleRegisterAccessOps};

// Convenient re-exports
pub use super::ResumeAction;
pub use super::{GdbInterrupt, ResumeAction};

/// Base debugging operations for single threaded targets.
#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -44,7 +44,7 @@ pub trait SingleThreadOps: Target {
fn resume(
&mut self,
action: ResumeAction,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<<Self::Arch as Arch>::Usize>, Self::Error>;

/// Support for the optimized [range stepping] resume action.
Expand Down Expand Up @@ -126,7 +126,7 @@ pub trait SingleThreadReverseCont: Target + SingleThreadOps {
/// Reverse-continue the target.
fn reverse_cont(
&mut self,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
}

Expand All @@ -141,7 +141,7 @@ pub trait SingleThreadReverseStep: Target + SingleThreadOps {
/// Reverse-step the target.
fn reverse_step(
&mut self,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
}

Expand Down Expand Up @@ -170,7 +170,7 @@ pub trait SingleThreadRangeStepping: Target + SingleThreadOps {
&mut self,
start: <Self::Arch as Arch>::Usize,
end: <Self::Arch as Arch>::Usize,
check_gdb_interrupt: &mut dyn FnMut() -> bool,
gdb_interrupt: GdbInterrupt<'_>,
) -> Result<StopReason<<Self::Arch as Arch>::Usize>, Self::Error>;
}

Expand Down

0 comments on commit 9e0526d

Please sign in to comment.