fixes & improvements

Former-commit-id: 1ddeb07bde78a676201037c0bb269ff8122d29c2
Former-commit-id: f9c0eaa2a1073dba82dfb4d3594fbc0b979ef2e0
This commit is contained in:
Michel Heily 2022-10-04 22:45:06 +03:00
parent 838ca43ac4
commit e4e2c710be
7 changed files with 76 additions and 29 deletions

View file

@ -13,9 +13,10 @@ pub extern crate gdbstub_arch;
/// Wait for tcp connection on port
pub fn wait_for_connection(port: u16) -> io::Result<TcpStream> {
let bind_addr = format!("0.0.0.0:{port}");
info!("waiting for connection on {:?}", bind_addr);
let sock = TcpListener::bind(bind_addr)?;
info!("waiting for connection");
// Blocks until a GDB client connects via TCP.
// i.e: Running `target remote localhost:<port>` from the GDB prompt.
let (stream, addr) = sock.accept()?;

View file

@ -41,7 +41,7 @@ impl<I: MemoryGdbInterface> SingleThreadBase for Arm7tdmiCore<I> {
&mut self,
regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
) -> TargetResult<(), Self> {
regs.pc = self.pc;
regs.pc = self.get_next_pc();
regs.lr = self.get_reg(REG_LR);
regs.sp = self.get_reg(REG_SP);
regs.r[..].copy_from_slice(&self.gpr[..13]);

View file

@ -32,6 +32,7 @@ pub(crate) enum DebuggerRequest {
Interrupt,
Resume,
SingleStep,
Reset,
Disconnected(DisconnectReason),
}
@ -40,7 +41,7 @@ pub(crate) struct DebuggerRequestHandler {
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
thread: JoinHandle<()>,
stopped: bool,
pub(crate) stopped: bool,
}
impl DebuggerRequestHandler {
@ -123,6 +124,12 @@ impl DebuggerRequestHandler {
self.stopped = false;
self.complete_request(None)
}
Reset => {
debug!("Sending reset interrupt to gba");
self.stopped = true;
gba.cpu.reset();
self.complete_request(Some(SingleThreadStopReason::Signal(Signal::SIGTRAP)))
}
SingleStep => {
debug!("Debugger requested single step");
self.stopped = true;
@ -153,6 +160,10 @@ impl DebuggerRequestHandler {
let mut finished = lock.lock().unwrap();
*finished = true;
cvar.notify_one();
// wait for the ack
while *finished {
finished = cvar.wait(finished).unwrap();
}
}
fn complete_request(

View file

@ -3,9 +3,8 @@ use std::sync::{Arc, Condvar, Mutex};
use arm7tdmi::{
gdb::wait_for_connection,
gdbstub::{
common::Signal,
conn::ConnectionExt,
stub::{GdbStub, SingleThreadStopReason},
stub::GdbStub,
},
};
@ -41,14 +40,12 @@ pub(crate) fn start_gdb_server_thread(
target.disconnect(disconnect_reason);
});
let mut debugger = DebuggerRequestHandler {
let debugger = DebuggerRequestHandler {
rx,
request_complete_signal,
stop_signal,
thread,
stopped: true,
};
debugger.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT));
Ok(debugger)
}

View file

@ -1,4 +1,4 @@
use std::sync::{Arc, Condvar, Mutex};
use std::sync::{Arc, Condvar, Mutex, WaitTimeoutResult};
use std::time::Duration;
/// Implementing the Target trait for gdbstub
@ -12,6 +12,7 @@ use gdbstub::target::ext::base::singlethread::{
use gdbstub::target::ext::base::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps};
use gdbstub::target::ext::base::BaseOps;
use gdbstub::target::ext::breakpoints::BreakpointsOps;
use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput};
use gdbstub::target::{self, Target, TargetError, TargetResult};
use gdbstub_arch::arm::reg::ArmCoreRegs;
@ -42,16 +43,15 @@ impl DebuggerTarget {
pub fn debugger_request(&mut self, req: DebuggerRequest) {
let (lock, cvar) = &*self.request_complete_signal;
let mut finished = lock.lock().unwrap();
*finished = false;
// now send the request
self.tx.send(req).unwrap();
// wait for the notification
while !*finished {
finished = cvar.wait(finished).unwrap();
}
// ack the other side we got the signal
*finished = false;
cvar.notify_one();
}
pub fn wait_for_stop_reason_timeout(
@ -60,24 +60,22 @@ impl DebuggerTarget {
) -> Option<SingleThreadStopReason<u32>> {
let (lock, cvar) = &*self.stop_signal;
let mut stop_reason = lock.lock().unwrap();
if let Some(stop_reason) = stop_reason.take() {
return Some(stop_reason);
}
let (mut stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
if timeout_result.timed_out() {
None
} else {
Some(stop_reason.take().expect("None is not expected here"))
let mut timeout_result: WaitTimeoutResult;
while stop_reason.is_none() {
(stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
if timeout_result.timed_out() {
return None;
}
}
Some(stop_reason.take().expect("None is not expected here"))
}
pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason<u32> {
let (lock, cvar) = &*self.stop_signal;
let mut stop_reason = lock.lock().unwrap();
if let Some(stop_reason) = stop_reason.take() {
return stop_reason;
while stop_reason.is_none() {
stop_reason = cvar.wait(stop_reason).unwrap();
}
let mut stop_reason = cvar.wait(stop_reason).unwrap();
stop_reason.take().expect("None is not expected here")
}
@ -106,6 +104,10 @@ impl Target for DebuggerTarget {
fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
Some(self)
}
fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
Some(self)
}
}
impl SingleThreadBase for DebuggerTarget {
@ -208,3 +210,31 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
Ok(true)
}
}
impl target::ext::monitor_cmd::MonitorCmd for DebuggerTarget {
fn handle_monitor_cmd(
&mut self,
cmd: &[u8],
mut out: ConsoleOutput<'_>,
) -> Result<(), Self::Error> {
let cmd = match std::str::from_utf8(cmd) {
Ok(cmd) => cmd,
Err(_) => {
outputln!(out, "command must be valid UTF-8");
return Ok(());
}
};
match cmd {
"reset" => {
self.debugger_request(DebuggerRequest::Reset);
outputln!(out, "sent reset signal");
}
unk => {
outputln!(out, "unknown command: {}", unk);
}
}
Ok(())
}
}

View file

@ -27,7 +27,6 @@ use rustboyadvance_core::prelude::*;
use rustboyadvance_utils::FpsCounter;
const LOG_DIR: &str = ".logs";
const DEFAULT_GDB_SERVER_PORT: u16 = 1337;
fn ask_download_bios() {
const OPEN_SOURCE_BIOS_URL: &'static str =
@ -113,7 +112,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
if opts.gdbserver {
gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT);
gba.start_gdbserver(opts.gdbserver_port);
}
let mut vsync = true;
@ -144,7 +143,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.unwrap();
info!("ending debugger...")
}
Scancode::F2 => gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT),
Scancode::F2 => gba.start_gdbserver(opts.gdbserver_port),
Scancode::F5 => {
info!("Saving state ...");
let save = gba.save_state()?;
@ -159,10 +158,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if opts.savestate_path().is_file() {
let save = read_bin_file(&opts.savestate_path())?;
info!("Restoring state from {:?}...", opts.savestate_path());
let (audio_interface, _sdl_audio_device_new) = audio::create_audio_player(&sdl_context)?;
let (audio_interface, _sdl_audio_device_new) =
audio::create_audio_player(&sdl_context)?;
_sdl_audio_device = _sdl_audio_device_new;
let rom = opts.read_rom()?.into_boxed_slice();
gba = Box::new(GameBoyAdvance::from_saved_state(&save, bios_bin.clone(), rom, audio_interface)?);
gba = Box::new(GameBoyAdvance::from_saved_state(
&save,
bios_bin.clone(),
rom,
audio_interface,
)?);
info!("Restored!");
} else {
info!("Savestate not created, please create one by pressing F5");

View file

@ -1,4 +1,4 @@
use std::{path::PathBuf, io};
use std::{io, path::PathBuf};
use rustboyadvance_core::{
cartridge::{BackupType, GamepakBuilder},
@ -33,6 +33,9 @@ pub struct Options {
#[structopt(short = "d", long)]
pub gdbserver: bool,
#[structopt(long = "port", default_value = "1337")]
pub gdbserver_port: u16,
/// Force emulation of RTC, use for games that have RTC but the emulator fails to detect
#[structopt(long)]
pub rtc: bool,