use core::{fmt::Write, str::SplitWhitespace};
use gdbstub::target::ext::{self, monitor_cmd::ConsoleOutput};
use crate::{
arch::{DebuggerArch, SystemArch},
memory,
system::SystemStateTrait,
};
use super::PatinaTarget;
const MONITOR_HELP: &str = "
Patina Rust Debugger monitor commands:
? - Display information about the state of the machine.
arch ... - Architecture specific commands.
mod ... - Commands for breaking on or quering modules.
help - Display this help.
reboot - Prepares to reboot the machine on the next continue.
toggle_uncached_access - Toggle accessing uncached memory regions.
By default uncached memory access is disabled.
";
const MOD_HELP: &str = "
Mod commands:
break [module] - Set load breakpoint for a module.
breakall - Break on all module loads.
clear - clear all module breakpoints.
list [count] [index] - List loaded modules.
";
impl ext::monitor_cmd::MonitorCmd for PatinaTarget {
fn handle_monitor_cmd(&mut self, cmd: &[u8], out: ConsoleOutput<'_>) -> Result<(), Self::Error> {
let cmd_str = core::str::from_utf8(cmd).map_err(|_| ())?;
let mut tokens: SplitWhitespace<'_> = cmd_str.split_whitespace();
let mut buf = MonitorBuffer::<'_, 128>::new(out);
let cmd = match tokens.next() {
Some(token) if token.starts_with("O[") && token.ends_with("]") => {
let offset_str = &token[2..token.len() - 1];
let offset: usize = offset_str.parse().ok().unwrap_or(0);
buf.set_start_offset(offset);
tokens.next()
}
other => other,
};
match cmd {
Some("help") | None => {
let _ = buf.write_str(MONITOR_HELP);
let _ = buf.write_str("External commands:\n");
if let Some(state) = self.system_state.try_lock() {
state.dump_monitor_commands(&mut buf);
};
}
Some("mod") => {
self.module_cmd(&mut tokens, &mut buf);
}
Some("reboot") | Some("R") => {
self.reboot = true;
let _ = buf.write_str("System will reboot on continue.");
}
Some("?") => {
let _ = write!(
buf,
concat!(
"Patina Rust Debugger ",
env!("CARGO_PKG_VERSION"),
"\n",
"Instruction Pointer: {:#X}\n",
"Exception Type: {}\n"
),
self.exception_info.instruction_pointer, self.exception_info.exception_type
);
}
Some("disablechecks") => {
self.disable_checks = true;
let _ = buf.write_str("Disabling safety checks. Good luck!");
}
Some("arch") => {
SystemArch::monitor_cmd(&mut tokens, &mut buf);
}
Some("toggle_uncached_access") => {
let enabled = memory::toggle_uncached_access();
if enabled {
let _ = buf.write_str("Uncached memory access enabled.");
} else {
let _ = buf.write_str("Uncached memory access disabled.");
}
}
Some(cmd) => match self.system_state.try_lock() {
Some(state) => {
if !state.handle_monitor_command(cmd, &mut tokens, &mut buf) {
let _ = buf.write_str("Unknown command. Use 'help' for a list of commands.");
}
}
None => {
let _ = buf.write_str("ERROR: Failed to acquire system state lock for monitor callbacks!");
}
},
}
Ok(())
}
}
impl PatinaTarget {
fn module_cmd(&mut self, tokens: &mut SplitWhitespace<'_>, out: &mut dyn Write) {
let mut state = match self.system_state.try_lock() {
Some(state) => state,
None => {
let _ = out.write_str("ERROR: Failed to acquire modules lock!");
return;
}
};
match tokens.next() {
Some("breakall") => {
state.set_module_breakpoint_all();
let _ = out.write_str("Will break for all module loads.");
}
Some("break") => {
for module in tokens.by_ref() {
state.add_module_breakpoint(module);
}
let _ = out.write_str("Module breakpoints:\n");
state.dump_module_breakpoints(out);
}
Some("clear") => {
state.clear_module_breakpoints();
let _ = out.write_str("Cleared module breaks!");
}
Some("list") => {
let count: usize = tokens.next().and_then(|token| token.parse().ok()).unwrap_or(usize::MAX);
let start: usize = tokens.next().and_then(|token| token.parse().ok()).unwrap_or(0);
let printed = state.dump_modules(out, start, count);
if printed == 0 {
let _ = out.write_str("No modules.");
}
}
_ => {
let _ = out.write_str(MOD_HELP);
}
}
}
}
struct MonitorBuffer<'a, const N: usize> {
buffer: [u8; N],
pos: usize,
start_offset: usize,
out: ConsoleOutput<'a>,
}
impl<'a, const N: usize> MonitorBuffer<'a, N> {
const fn new(out: ConsoleOutput<'a>) -> Self {
MonitorBuffer { buffer: [0; N], pos: 0, start_offset: 0, out }
}
fn set_start_offset(&mut self, offset: usize) {
self.start_offset = offset;
}
fn flush(&mut self) {
if self.pos > 0 {
let data = &self.buffer[0..self.pos];
self.out.write_raw(data);
self.pos = 0;
}
}
}
impl<const N: usize> Write for MonitorBuffer<'_, N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let mut data = s.as_bytes();
let mut len = data.len();
if self.start_offset > 0 {
if self.start_offset >= len {
self.start_offset -= len;
return Ok(());
} else {
data = data.get(self.start_offset..).ok_or(core::fmt::Error)?;
len = data.len();
self.start_offset = 0; }
}
if len < N {
if len > N - self.pos {
self.flush();
}
if let Some(dest) = self.buffer.get_mut(self.pos..self.pos + len) {
dest.copy_from_slice(data);
self.pos += len;
}
} else {
self.flush();
self.out.write_raw(data);
}
Ok(())
}
}
impl<const N: usize> Drop for MonitorBuffer<'_, N> {
fn drop(&mut self) {
self.flush();
}
}