From 17d38111245c4dbe761877a1931251b84af03893 Mon Sep 17 00:00:00 2001 From: Simon Gellis Date: Fri, 17 Jan 2025 22:00:46 -0500 Subject: [PATCH] No debugging unless the game is running --- src/emulator.rs | 4 ++++ src/gdbserver.rs | 47 ++++++++++++++++++++++++++++++++++++++--------- src/main.rs | 9 ++++++--- src/window/gdb.rs | 8 ++++++-- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/emulator.rs b/src/emulator.rs index 117d5fa..aa065b0 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -305,6 +305,10 @@ impl Emulator { } fn start_debugging(&mut self, sim_id: SimId, sender: DebugSender) { + if self.sim_state[sim_id.to_index()].load(Ordering::Acquire) != SimState::Ready { + // Can't debug unless a game is connected + return; + } let debug = DebugInfo { sender, stop_reason: Some(DebugStopReason::Trapped), diff --git a/src/gdbserver.rs b/src/gdbserver.rs index 09a9eb8..e5a6231 100644 --- a/src/gdbserver.rs +++ b/src/gdbserver.rs @@ -9,7 +9,7 @@ use std::{ use tokio::{ io::{AsyncWriteExt as _, BufReader}, net::{TcpListener, TcpStream}, - select, + pin, select, sync::{mpsc, oneshot}, }; use tracing::{debug, enabled, error, info, Level}; @@ -57,8 +57,9 @@ impl GdbServer { .unwrap() .block_on(async move { select! { - _ = run_server(sim_id, client, port, &status) => {} + _ = run_server(sim_id, client.clone(), port, &status) => {} _ = rx => { + client.send_command(EmulatorCommand::StopDebugging(sim_id)); *status.lock().unwrap() = GdbServerStatus::Stopped; } } @@ -73,19 +74,46 @@ impl GdbServer { } } +impl Drop for GdbServer { + fn drop(&mut self) { + self.stop(); + } +} + async fn run_server( sim_id: SimId, client: EmulatorClient, port: u16, status: &Mutex, ) { + let (debug_sink, mut debug_source) = mpsc::unbounded_channel(); + client.send_command(EmulatorCommand::StartDebugging(sim_id, debug_sink)); + info!("Connecting to debugger on port {port}..."); - let Some(stream) = try_connect(port, status).await else { - return; + let connect_future = try_connect(port, status); + pin!(connect_future); + + let stream = loop { + select! { + stream = &mut connect_future => { + if let Some(stream) = stream { + break stream; + } else { + return; + } + } + event = debug_source.recv() => { + if event.is_none() { + // The sim has stopped (or was never started) + *status.lock().unwrap() = GdbServerStatus::Stopped; + return; + } + } + } }; info!("Connected!"); let mut connection = GdbConnection::new(sim_id, client); - match connection.run(stream).await { + match connection.run(stream, debug_source).await { Ok(()) => { info!("Finished debugging."); *status.lock().unwrap() = GdbServerStatus::Stopped; @@ -154,12 +182,13 @@ impl GdbConnection { memory_buf: None, } } - async fn run(&mut self, stream: TcpStream) -> Result<()> { - let (debug_sink, mut debug_source) = mpsc::unbounded_channel(); + async fn run( + &mut self, + stream: TcpStream, + mut debug_source: mpsc::UnboundedReceiver, + ) -> Result<()> { let (rx, mut tx) = stream.into_split(); let mut request_source = RequestSource::new(BufReader::new(rx)); - self.client - .send_command(EmulatorCommand::StartDebugging(self.sim_id, debug_sink)); loop { let response = select! { maybe_event = debug_source.recv() => { diff --git a/src/main.rs b/src/main.rs index 4ee7949..33ae747 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::{path::PathBuf, process, time::SystemTime}; -use anyhow::Result; +use anyhow::{bail, Result}; use app::Application; use clap::Parser; use emulator::EmulatorBuilder; @@ -95,10 +95,13 @@ fn main() -> Result<()> { let args = Args::parse(); let (mut builder, client) = EmulatorBuilder::new(); - if let Some(path) = args.rom { - builder = builder.with_rom(&path); + if let Some(path) = &args.rom { + builder = builder.with_rom(path); } if args.debug_port.is_some() { + if args.rom.is_none() { + bail!("to start debugging, please select a game."); + } builder = builder.start_paused(true); } diff --git a/src/window/gdb.rs b/src/window/gdb.rs index 50d1910..e89de02 100644 --- a/src/window/gdb.rs +++ b/src/window/gdb.rs @@ -3,7 +3,7 @@ use winit::event_loop::EventLoopProxy; use crate::{ app::UserEvent, - emulator::{EmulatorClient, SimId}, + emulator::{EmulatorClient, SimId, SimState}, gdbserver::{GdbServer, GdbServerStatus}, }; @@ -11,6 +11,7 @@ use super::AppWindow; pub struct GdbServerWindow { sim_id: SimId, + client: EmulatorClient, port_str: String, connected: bool, quit_on_disconnect: bool, @@ -22,6 +23,7 @@ impl GdbServerWindow { pub fn new(sim_id: SimId, client: EmulatorClient, proxy: EventLoopProxy) -> Self { Self { sim_id, + client: client.clone(), port_str: (8080 + sim_id.to_index()).to_string(), connected: false, quit_on_disconnect: false, @@ -78,7 +80,9 @@ impl AppWindow for GdbServerWindow { self.connected = false; } let start_button = Button::new("Start"); - if ui.add_enabled(port_num.is_some(), start_button).clicked() { + let can_start = + port_num.is_some() && self.client.sim_state(self.sim_id) == SimState::Ready; + if ui.add_enabled(can_start, start_button).clicked() { let port = port_num.unwrap(); self.server.start(port); self.connected = true;