Skip to content

Commit 3caf3dc

Browse files
committed
feat: add API to send text to GDB client
Only from the Running state, as the implementation is based on the `O xx` stop reply packet. Closes #51
1 parent bd8bd04 commit 3caf3dc

4 files changed

Lines changed: 81 additions & 0 deletions

File tree

examples/armv4t/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ impl run_blocking::BlockingEventLoop for EmuGdbEventLoop {
113113
Ok(simple_stub.incoming_data(target, byte))
114114
}
115115
emu::RunEvent::Event(event) => {
116+
// example of using the console writer to print a message to the GDB console
117+
// when the target finishes running.
118+
if matches!(event, emu::Event::Halted) {
119+
let mut out = simple_stub.console_writer(target);
120+
let ret = target.cpu.reg_get(armv4t_emu::Mode::User, 0);
121+
gdbstub::outputln!(out, "Program completed. Return value: {}", ret);
122+
}
123+
116124
// translate emulator stop reason into GDB stop reason
117125
Ok(simple_stub.report_stop(target, |report_stop| {
118126
use gdbstub::target::ext::breakpoints::WatchKind;

examples/armv4t_multicore/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ impl run_blocking::BlockingEventLoop for EmuGdbEventLoop {
114114
Ok(simple_stub.incoming_data(target, byte))
115115
}
116116
emu::RunEvent::Event(event, cpuid) => {
117+
// example of using the console writer to print a message to the GDB console
118+
// when the target finishes running.
119+
if matches!(event, emu::Event::Halted) {
120+
let mut out = simple_stub.console_writer(target);
121+
let ret = target.cpu.reg_get(armv4t_emu::Mode::User, 0);
122+
gdbstub::outputln!(out, "Program completed. Return value: {}", ret);
123+
}
124+
117125
// translate emulator stop reason into GDB stop reason
118126
Ok(simple_stub.report_stop(target, |report_stop| {
119127
use gdbstub::target::ext::breakpoints::WatchKind;

src/stub/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ pub mod run_blocking {
7272
}
7373
}
7474

75+
/// Return a writer that can be used to write arbitrary text to the GDB
76+
/// client console.
77+
pub fn console_writer(
78+
&mut self,
79+
target: &mut T,
80+
) -> state_machine::GdbConsoleWriter<'_, 'a, T, C> {
81+
self.gdb.console_writer(target)
82+
}
83+
7584
/// Pass a byte to the GDB stub.
7685
pub fn incoming_data(self, target: &mut T, byte: u8) -> Event<'a, T, C> {
7786
Event {

src/stub/state_machine.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,53 @@ where
812812
}
813813
}
814814

815+
/// A helper struct which enables writing arbitrary text to the GDB client
816+
/// console (either via `write_raw`, or via the `core::fmt::Write`
817+
/// implementation + [`output!`](crate::output) macro).
818+
pub struct GdbConsoleWriter<'gdb, 'a, T, C>
819+
where
820+
T: Target,
821+
C: Connection,
822+
{
823+
gdb: &'gdb mut GdbStubStateMachineInner<'a, state::Running, T, C>,
824+
use_rle: bool,
825+
}
826+
827+
impl<'gdb, 'a, T, C> GdbConsoleWriter<'gdb, 'a, T, C>
828+
where
829+
T: Target,
830+
C: Connection,
831+
{
832+
/// Write raw data to the GDB client console.
833+
///
834+
/// Unless you have a specific reason to use this method, you will likely be
835+
/// better served by using the `core::fmt::Write` implementation +
836+
/// [`output!`](crate::output) macros instead.
837+
///
838+
/// The GDB RSP docs recommend that console output be ASCII, but in
839+
/// practice, GDB seems to be fine with receiving arbitrary binary data here
840+
/// (e.g: it's possible to write UTF-8 data just fine, and it renders
841+
/// correctly in GDB's console).
842+
pub fn write_raw(&mut self, data: &[u8]) -> Result<(), GdbStubError<T::Error, C::Error>> {
843+
let mut res = ResponseWriter::new(self.gdb.borrow_conn(), self.use_rle);
844+
res.write_str("O").map_err(InternalError::from)?;
845+
res.write_hex_buf(data).map_err(InternalError::from)?;
846+
res.flush().map_err(InternalError::from)?;
847+
Ok(())
848+
}
849+
}
850+
851+
impl<'gdb, 'a, T, C> core::fmt::Write for GdbConsoleWriter<'gdb, 'a, T, C>
852+
where
853+
T: Target,
854+
C: Connection,
855+
{
856+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
857+
self.write_raw(s.as_bytes())
858+
.map_err(|_| core::fmt::Error {})
859+
}
860+
}
861+
815862
/// Methods which can only be called from the
816863
/// [`GdbStubStateMachine::Running`] state.
817864
impl<'a, T, C> GdbStubStateMachineInner<'a, state::Running, T, C>
@@ -831,6 +878,15 @@ where
831878
}
832879
}
833880

881+
/// Return a struct that can be used to write arbitrary text to the GDB
882+
/// client console.
883+
pub fn console_writer(&mut self, target: &mut T) -> GdbConsoleWriter<'_, 'a, T, C> {
884+
GdbConsoleWriter {
885+
gdb: self,
886+
use_rle: target.use_rle(),
887+
}
888+
}
889+
834890
/// Pass a byte to the GDB stub.
835891
pub fn incoming_data(
836892
mut self,

0 commit comments

Comments
 (0)