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

View file

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

View file

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

View file

@ -3,9 +3,8 @@ use std::sync::{Arc, Condvar, Mutex};
use arm7tdmi::{ use arm7tdmi::{
gdb::wait_for_connection, gdb::wait_for_connection,
gdbstub::{ gdbstub::{
common::Signal,
conn::ConnectionExt, conn::ConnectionExt,
stub::{GdbStub, SingleThreadStopReason}, stub::GdbStub,
}, },
}; };
@ -41,14 +40,12 @@ pub(crate) fn start_gdb_server_thread(
target.disconnect(disconnect_reason); target.disconnect(disconnect_reason);
}); });
let mut debugger = DebuggerRequestHandler { let debugger = DebuggerRequestHandler {
rx, rx,
request_complete_signal, request_complete_signal,
stop_signal, stop_signal,
thread, thread,
stopped: true, stopped: true,
}; };
debugger.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT));
Ok(debugger) 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; use std::time::Duration;
/// Implementing the Target trait for gdbstub /// 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::singlethread::{SingleThreadResumeOps, SingleThreadSingleStepOps};
use gdbstub::target::ext::base::BaseOps; use gdbstub::target::ext::base::BaseOps;
use gdbstub::target::ext::breakpoints::BreakpointsOps; use gdbstub::target::ext::breakpoints::BreakpointsOps;
use gdbstub::target::ext::monitor_cmd::{outputln, ConsoleOutput};
use gdbstub::target::{self, Target, TargetError, TargetResult}; use gdbstub::target::{self, Target, TargetError, TargetResult};
use gdbstub_arch::arm::reg::ArmCoreRegs; use gdbstub_arch::arm::reg::ArmCoreRegs;
@ -42,16 +43,15 @@ impl DebuggerTarget {
pub fn debugger_request(&mut self, req: DebuggerRequest) { pub fn debugger_request(&mut self, req: DebuggerRequest) {
let (lock, cvar) = &*self.request_complete_signal; let (lock, cvar) = &*self.request_complete_signal;
let mut finished = lock.lock().unwrap(); let mut finished = lock.lock().unwrap();
*finished = false;
// now send the request // now send the request
self.tx.send(req).unwrap(); self.tx.send(req).unwrap();
// wait for the notification // wait for the notification
while !*finished { while !*finished {
finished = cvar.wait(finished).unwrap(); finished = cvar.wait(finished).unwrap();
} }
// ack the other side we got the signal
*finished = false; *finished = false;
cvar.notify_one();
} }
pub fn wait_for_stop_reason_timeout( pub fn wait_for_stop_reason_timeout(
@ -60,24 +60,22 @@ impl DebuggerTarget {
) -> Option<SingleThreadStopReason<u32>> { ) -> Option<SingleThreadStopReason<u32>> {
let (lock, cvar) = &*self.stop_signal; let (lock, cvar) = &*self.stop_signal;
let mut stop_reason = lock.lock().unwrap(); let mut stop_reason = lock.lock().unwrap();
if let Some(stop_reason) = stop_reason.take() { let mut timeout_result: WaitTimeoutResult;
return Some(stop_reason); while stop_reason.is_none() {
} (stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
let (mut stop_reason, timeout_result) = cvar.wait_timeout(stop_reason, timeout).unwrap();
if timeout_result.timed_out() { if timeout_result.timed_out() {
None return None;
} else {
Some(stop_reason.take().expect("None is not expected here"))
} }
} }
Some(stop_reason.take().expect("None is not expected here"))
}
pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason<u32> { pub fn wait_for_stop_reason_blocking(&mut self) -> SingleThreadStopReason<u32> {
let (lock, cvar) = &*self.stop_signal; let (lock, cvar) = &*self.stop_signal;
let mut stop_reason = lock.lock().unwrap(); let mut stop_reason = lock.lock().unwrap();
if let Some(stop_reason) = stop_reason.take() { while stop_reason.is_none() {
return stop_reason; 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") 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>> { fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
Some(self) Some(self)
} }
fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
Some(self)
}
} }
impl SingleThreadBase for DebuggerTarget { impl SingleThreadBase for DebuggerTarget {
@ -208,3 +210,31 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
Ok(true) 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; use rustboyadvance_utils::FpsCounter;
const LOG_DIR: &str = ".logs"; const LOG_DIR: &str = ".logs";
const DEFAULT_GDB_SERVER_PORT: u16 = 1337;
fn ask_download_bios() { fn ask_download_bios() {
const OPEN_SOURCE_BIOS_URL: &'static str = const OPEN_SOURCE_BIOS_URL: &'static str =
@ -113,7 +112,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
if opts.gdbserver { if opts.gdbserver {
gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT); gba.start_gdbserver(opts.gdbserver_port);
} }
let mut vsync = true; let mut vsync = true;
@ -144,7 +143,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.unwrap(); .unwrap();
info!("ending debugger...") info!("ending debugger...")
} }
Scancode::F2 => gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT), Scancode::F2 => gba.start_gdbserver(opts.gdbserver_port),
Scancode::F5 => { Scancode::F5 => {
info!("Saving state ..."); info!("Saving state ...");
let save = gba.save_state()?; let save = gba.save_state()?;
@ -159,10 +158,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
if opts.savestate_path().is_file() { if opts.savestate_path().is_file() {
let save = read_bin_file(&opts.savestate_path())?; let save = read_bin_file(&opts.savestate_path())?;
info!("Restoring state from {:?}...", 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; _sdl_audio_device = _sdl_audio_device_new;
let rom = opts.read_rom()?.into_boxed_slice(); 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!"); info!("Restored!");
} else { } else {
info!("Savestate not created, please create one by pressing F5"); 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::{ use rustboyadvance_core::{
cartridge::{BackupType, GamepakBuilder}, cartridge::{BackupType, GamepakBuilder},
@ -33,6 +33,9 @@ pub struct Options {
#[structopt(short = "d", long)] #[structopt(short = "d", long)]
pub gdbserver: bool, 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 /// Force emulation of RTC, use for games that have RTC but the emulator fails to detect
#[structopt(long)] #[structopt(long)]
pub rtc: bool, pub rtc: bool,