Gdb fixes

Former-commit-id: f8507472a94386902962d1ffead7dd6f85624d82
Former-commit-id: 15446ef199288e887b7bc02fec08f9798c4c6ea2
This commit is contained in:
Michel Heily 2022-09-20 23:58:54 +03:00
parent c8c1cdd57b
commit 838ca43ac4
9 changed files with 238 additions and 237 deletions

View file

@ -95,6 +95,7 @@ impl fmt::Display for CpuState {
} }
#[derive(Debug, Primitive, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Primitive, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum CpuMode { pub enum CpuMode {
User = 0b10000, User = 0b10000,
Fiq = 0b10001, Fiq = 0b10001,

View file

@ -2,7 +2,6 @@
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use arm7tdmi::gdbstub::stub::SingleThreadStopReason;
use bincode; use bincode;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -251,19 +250,9 @@ impl GameBoyAdvance {
/// Recv & handle messages from the debugger, and return if we are stopped or not /// Recv & handle messages from the debugger, and return if we are stopped or not
pub fn debugger_run(&mut self) { pub fn debugger_run(&mut self) {
let mut should_interrupt_frame = false;
let debugger = self.debugger.take().expect("debugger should be None here"); let debugger = self.debugger.take().expect("debugger should be None here");
self.debugger = debugger.handle_incoming_requests(self, &mut should_interrupt_frame); self.debugger = debugger.handle_incoming_requests(self);
self.frame_interruptible();
if let Some(debugger) = &mut self.debugger {
if should_interrupt_frame {
debugger.notify_stop_reason(SingleThreadStopReason::DoneStep);
} else {
self.frame_interruptible();
}
} else {
error!("debugger was disconnected!");
}
} }
#[inline] #[inline]
@ -272,10 +261,15 @@ impl GameBoyAdvance {
} }
#[inline] #[inline]
pub(crate) fn cpu_step(&mut self) { fn cpu_interrupt(&mut self) {
self.cpu.irq();
self.io_devs.haltcnt = HaltState::Running; // Clear out from low power mode
}
#[inline]
fn cpu_step(&mut self) {
if self.io_devs.intc.irq_pending() { if self.io_devs.intc.irq_pending() {
self.cpu.irq(); self.cpu_interrupt();
self.io_devs.haltcnt = HaltState::Running;
} }
self.cpu.step(); self.cpu.step();
} }
@ -285,7 +279,29 @@ impl GameBoyAdvance {
match (self.io_devs.dmac.is_active(), self.io_devs.haltcnt) { match (self.io_devs.dmac.is_active(), self.io_devs.haltcnt) {
(true, _) => Some(BusMaster::Dma), (true, _) => Some(BusMaster::Dma),
(false, HaltState::Running) => Some(BusMaster::Cpu), (false, HaltState::Running) => Some(BusMaster::Cpu),
(false, _) => None, (false, HaltState::Halt) => None,
}
}
#[inline]
pub(crate) fn single_step(&mut self) {
// 3 Options:
// 1. DMA is active - thus CPU is blocked
// 2. DMA inactive and halt state is RUN - CPU can run
// 3. DMA inactive and halt state is HALT - CPU is blocked
match self.get_bus_master() {
Some(BusMaster::Dma) => self.dma_step(),
Some(BusMaster::Cpu) => self.cpu_step(),
None => {
// Halt mode - system is in a low-power mode, only (IE and IF) can release CPU from this state.
if self.io_devs.intc.irq_pending() {
self.cpu_interrupt();
} else {
// Fast-forward to next pending HW event so we don't waste time idle-looping when we know the only way
// To get out of Halt mode is through an interrupt.
self.scheduler.fast_forward_to_next();
}
}
} }
} }
@ -293,60 +309,45 @@ impl GameBoyAdvance {
/// @return number of cycle actually ran /// @return number of cycle actually ran
#[inline] #[inline]
pub(super) fn run<const CHECK_BREAKPOINTS: bool>(&mut self, cycles_to_run: usize) -> usize { pub(super) fn run<const CHECK_BREAKPOINTS: bool>(&mut self, cycles_to_run: usize) -> usize {
let run_start_time = self.scheduler.timestamp(); let start_time = self.scheduler.timestamp();
let end_time = start_time + cycles_to_run;
// Register an event to mark the end of this run // Register an event to mark the end of this run
self.scheduler self.scheduler
.schedule_at(EventType::RunLimitReached, run_start_time + cycles_to_run); .schedule_at(EventType::RunLimitReached, end_time);
let mut running = true; 'running: loop {
while running { // The tricky part is to avoid unnecessary calls for Scheduler::handle_events,
// The tricky part is to avoid unnecessary calls for Scheduler::process_pending,
// performance-wise it would be best to run as many cycles as fast as possible while we know there are no pending events. // performance-wise it would be best to run as many cycles as fast as possible while we know there are no pending events.
// Fast forward emulation until an event occurs // Safety: Since we pushed a RunLimitReached event, we know this check has a hard limit
'run_unitl_next_event: while self.scheduler.timestamp() while self.scheduler.timestamp()
<= self.scheduler.timestamp_of_next_event() <= unsafe { self.scheduler.timestamp_of_next_event_unchecked() }
{ {
// 3 Options: self.single_step();
// 1. DMA is active - thus CPU is blocked if CHECK_BREAKPOINTS {
// 2. DMA inactive and halt state is RUN - CPU can run if let Some(bp) = self.cpu.check_breakpoint() {
// 3. DMA inactive and halt state is HALT - CPU is blocked debug!("Arm7tdmi breakpoint hit 0x{:08x}", bp);
match self.get_bus_master() { self.scheduler.cancel_pending(EventType::RunLimitReached);
Some(BusMaster::Dma) => self.dma_step(), let _ = self.handle_events();
Some(BusMaster::Cpu) => { if let Some(debugger) = &mut self.debugger {
self.cpu_step(); debugger.notify_breakpoint(bp);
if CHECK_BREAKPOINTS {
if let Some(bp) = self.cpu.check_breakpoint() {
debug!("Arm7tdmi breakpoint hit 0x{:08x}", bp);
self.scheduler.cancel_pending(EventType::RunLimitReached);
running = false;
if let Some(debugger) = &mut self.debugger {
debugger.notify_breakpoint(bp);
}
break 'run_unitl_next_event;
}
}
}
None => {
if self.io_devs.intc.irq_pending() {
self.io_devs.haltcnt = HaltState::Running;
} else {
self.scheduler.fast_forward_to_next();
self.handle_events(&mut running)
} }
break 'running;
} }
} }
} }
self.handle_events(&mut running); if self.handle_events() {
break 'running;
}
} }
self.scheduler.timestamp() - run_start_time self.scheduler.timestamp() - start_time
} }
fn handle_events(&mut self, run_limit_flag: &mut bool) { /// Handle all pending scheduler events and return if run limit was reached.
#[inline]
pub(super) fn handle_events(&mut self) -> bool {
let io = &mut (*self.io_devs); let io = &mut (*self.io_devs);
while let Some((event, event_time)) = self.scheduler.pop_pending_event() { while let Some((event, event_time)) = self.scheduler.pop_pending_event() {
// Since we only examine the scheduler queue every so often, most events will be handled late by a few cycles. // Since we only examine the scheduler queue every so often, most events will be handled late by a few cycles.
@ -354,8 +355,8 @@ impl GameBoyAdvance {
// every cpu cycle, where in 99% of cases it will always be empty. // every cpu cycle, where in 99% of cases it will always be empty.
let new_event = match event { let new_event = match event {
EventType::RunLimitReached => { EventType::RunLimitReached => {
*run_limit_flag = false; // If we have pending events, we handle by the next frame.
None return true;
} }
EventType::DmaActivateChannel(channel_id) => { EventType::DmaActivateChannel(channel_id) => {
io.dmac.activate_channel(channel_id); io.dmac.activate_channel(channel_id);
@ -375,6 +376,7 @@ impl GameBoyAdvance {
self.scheduler.schedule_at(new_event, event_time + when) self.scheduler.schedule_at(new_event, event_time + when)
} }
} }
false
} }
pub fn skip_bios(&mut self) { pub fn skip_bios(&mut self) {
@ -414,26 +416,6 @@ impl GameBoyAdvance {
None None
} }
#[cfg(feature = "debugger")]
/// 'step' function that checks for breakpoints
/// TODO avoid code duplication
pub fn step_debugger(&mut self) -> Option<u32> {
// clear any pending DMAs
self.dma_step();
// Run the CPU
self.cpu_step();
let breakpoint = self.check_breakpoint();
let mut _running = true;
while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() {
self.handle_event(event, cycles_late, &mut _running);
}
breakpoint
}
pub fn get_frame_buffer(&self) -> &[u32] { pub fn get_frame_buffer(&self) -> &[u32] {
self.sysbus.io.gpu.get_frame_buffer() self.sysbus.io.gpu.get_frame_buffer()
} }

View file

@ -9,13 +9,14 @@ use arm7tdmi::gdbstub::target::TargetError;
use arm7tdmi::gdbstub::target::{ext::base::singlethread::SingleThreadBase, Target}; use arm7tdmi::gdbstub::target::{ext::base::singlethread::SingleThreadBase, Target};
use arm7tdmi::gdbstub_arch::arm::reg::ArmCoreRegs; use arm7tdmi::gdbstub_arch::arm::reg::ArmCoreRegs;
use arm7tdmi::memory::Addr; use arm7tdmi::memory::Addr;
use crossbeam::channel::{Receiver, Sender}; use crossbeam::channel::Receiver;
// mod target; // mod target;
mod event_loop; mod event_loop;
pub(crate) mod gdb_thread; pub(crate) mod gdb_thread;
mod memory_map; mod memory_map;
mod target; mod target;
use target::DebuggerTarget;
use crate::GameBoyAdvance; use crate::GameBoyAdvance;
@ -34,153 +35,119 @@ pub(crate) enum DebuggerRequest {
Disconnected(DisconnectReason), Disconnected(DisconnectReason),
} }
pub struct DebuggerTarget {
tx: Sender<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, Condvar)>,
memory_map: String,
}
impl DebuggerTarget {
#[inline]
pub fn wait_for_operation(&mut self) {
let (lock, cvar) = &*self.request_complete_signal;
let mut finished = lock.lock().unwrap();
while !*finished {
finished = cvar.wait(finished).unwrap();
}
*finished = false;
}
}
pub(crate) struct DebuggerRequestHandler { pub(crate) struct DebuggerRequestHandler {
rx: Receiver<DebuggerRequest>, rx: Receiver<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>, request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, Condvar)>, stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
thread: JoinHandle<()>, thread: JoinHandle<()>,
stopped: bool, stopped: bool,
} }
enum DebuggerStatus {
RequestComplete,
ResumeRequested,
StopRequested,
Disconnected(DisconnectReason),
}
impl DebuggerRequestHandler { impl DebuggerRequestHandler {
pub fn handle_incoming_requests(
mut self,
gba: &mut GameBoyAdvance,
) -> Option<DebuggerRequestHandler> {
if self.thread.is_finished() {
warn!("gdb server thread unexpectdly died");
return self.terminate();
}
loop {
// Handle as much as messages as possible
while let Ok(mut req) = self.rx.try_recv() {
match self.handle_request(gba, &mut req) {
Ok(Some(disconnect_reason)) => {
debug!("Debugger disconnected due to {:?}", disconnect_reason);
debug!("closing gdbserver thread");
return self.terminate();
}
Ok(None) => {}
Err(_) => {
error!("An error occured while handling debug request {:?}", req);
return self.terminate();
}
}
}
// If target is in stopped state, stay here.
if !self.stopped {
break;
}
}
Some(self)
}
fn handle_request( fn handle_request(
&mut self, &mut self,
gba: &mut GameBoyAdvance, gba: &mut GameBoyAdvance,
req: &mut DebuggerRequest, req: &mut DebuggerRequest,
) -> Result<DebuggerStatus, TargetError<<DebuggerTarget as Target>::Error>> { ) -> Result<Option<DisconnectReason>, TargetError<<DebuggerTarget as Target>::Error>> {
use DebuggerRequest::*; use DebuggerRequest::*;
match req { match req {
ReadRegs(regs) => { ReadRegs(regs) => {
let mut regs = regs.lock().unwrap(); let mut regs = regs.lock().unwrap();
gba.cpu.read_registers(&mut regs)?; gba.cpu.read_registers(&mut regs)?;
debug!("Debugger requested to read regs: {:?}", regs); trace!("Debugger requested to read regs: {:?}", regs);
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
WriteRegs(regs) => { WriteRegs(regs) => {
debug!("Debugger requested to write regs: {:?}", regs); trace!("Debugger requested to write regs: {:?}", regs);
gba.cpu.write_registers(regs)?; gba.cpu.write_registers(regs)?;
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
ReadAddrs(addr, data) => { ReadAddrs(addr, data) => {
let mut data = data.lock().unwrap(); let mut data = data.lock().unwrap();
debug!( trace!(
"Debugger requested to read {} bytes from 0x{:08x}", "Debugger requested to read {} bytes from 0x{:08x}",
data.len(), data.len(),
addr addr
); );
gba.cpu.read_addrs(*addr, &mut data)?; gba.cpu.read_addrs(*addr, &mut data)?;
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
WriteAddrs(addr, data) => { WriteAddrs(addr, data) => {
debug!( trace!(
"Debugger requested to write {} bytes at 0x{:08x}", "Debugger requested to write {} bytes at 0x{:08x}",
data.len(), data.len(),
addr addr
); );
gba.cpu.write_addrs(*addr, &data)?; gba.cpu.write_addrs(*addr, &data)?;
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
Interrupt => { Interrupt => {
debug!("Debugger requested stopped"); debug!("Ctrl-C from debugger");
self.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT)); self.stopped = true;
Ok(DebuggerStatus::StopRequested) self.complete_request(Some(SingleThreadStopReason::Signal(Signal::SIGINT)))
} }
Resume => { Resume => {
debug!("Debugger requested resume"); debug!("Resume");
self.stopped = false; self.stopped = false;
Ok(DebuggerStatus::ResumeRequested) self.complete_request(None)
} }
SingleStep => { SingleStep => {
debug!("Debugger requested single step"); debug!("Debugger requested single step");
gba.cpu_step(); self.stopped = true;
let stop_reason = SingleThreadStopReason::DoneStep; gba.single_step();
self.notify_stop_reason(stop_reason); let _ = gba.handle_events();
Ok(DebuggerStatus::StopRequested) self.complete_request(Some(SingleThreadStopReason::DoneStep))
} }
AddSwBreakpoint(addr) => { AddSwBreakpoint(addr) => {
gba.cpu.add_breakpoint(*addr); gba.cpu.add_breakpoint(*addr);
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
DelSwBreakpoint(addr) => { DelSwBreakpoint(addr) => {
gba.cpu.del_breakpoint(*addr); gba.cpu.del_breakpoint(*addr);
Ok(DebuggerStatus::RequestComplete) self.complete_request(None)
} }
Disconnected(reason) => Ok(DebuggerStatus::Disconnected(*reason)), Disconnected(reason) => Ok(Some(*reason)),
} }
} }
fn terminate(mut self, should_interrupt_frame: &mut bool) -> Option<DebuggerRequestHandler> { fn terminate(mut self) -> Option<DebuggerRequestHandler> {
self.notify_stop_reason(SingleThreadStopReason::Exited(1)); self.notify_stop_reason(SingleThreadStopReason::Exited(1));
self.thread.join().unwrap(); self.thread.join().unwrap();
self.stopped = true;
*should_interrupt_frame = true;
None None
} }
pub fn handle_incoming_requests(
mut self,
gba: &mut GameBoyAdvance,
should_interrupt_frame: &mut bool,
) -> Option<DebuggerRequestHandler> {
if self.thread.is_finished() {
warn!("gdb server thread unexpectdly died");
return self.terminate(should_interrupt_frame);
}
while let Ok(mut req) = self.rx.try_recv() {
match self.handle_request(gba, &mut req) {
Ok(DebuggerStatus::RequestComplete) => {
self.notify_request_complete();
}
Ok(DebuggerStatus::StopRequested) => {
self.stopped = true;
self.notify_request_complete();
}
Ok(DebuggerStatus::ResumeRequested) => {
self.stopped = false;
self.notify_request_complete();
}
Ok(DebuggerStatus::Disconnected(reason)) => {
debug!("Debugger disconnected due to {:?}", reason);
debug!("closing gdbserver thread");
return self.terminate(should_interrupt_frame);
}
Err(_) => {
error!("An error occured while handling debug request {:?}", req);
return self.terminate(should_interrupt_frame);
}
}
}
*should_interrupt_frame = self.stopped;
Some(self)
}
fn notify_request_complete(&mut self) { fn notify_request_complete(&mut self) {
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();
@ -188,15 +155,27 @@ impl DebuggerRequestHandler {
cvar.notify_one(); cvar.notify_one();
} }
fn complete_request(
&mut self,
stop_reason: Option<SingleThreadStopReason<u32>>,
) -> Result<Option<DisconnectReason>, TargetError<<DebuggerTarget as Target>::Error>> {
self.notify_request_complete();
if let Some(stop_reason) = stop_reason {
self.notify_stop_reason(stop_reason);
}
Ok(None)
}
pub fn notify_stop_reason(&mut self, reason: SingleThreadStopReason<u32>) { pub fn notify_stop_reason(&mut self, reason: SingleThreadStopReason<u32>) {
self.stopped = true; debug!("Notifying debugger on stop reason: {:?}", reason);
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();
*stop_reason = reason; *stop_reason = Some(reason);
cvar.notify_one(); cvar.notify_one();
} }
pub fn notify_breakpoint(&mut self, _bp: Addr) { pub fn notify_breakpoint(&mut self, _bp: Addr) {
self.stopped = true;
self.notify_stop_reason(SingleThreadStopReason::SwBreak(())); self.notify_stop_reason(SingleThreadStopReason::SwBreak(()));
} }
} }

View file

@ -6,9 +6,9 @@ use arm7tdmi::gdb::gdbstub::{
target::Target, target::Target,
}; };
use super::{DebuggerRequest, DebuggerTarget}; use super::{target::DebuggerTarget, DebuggerRequest};
pub struct DebuggerEventLoop {} pub(crate) struct DebuggerEventLoop {}
impl run_blocking::BlockingEventLoop for DebuggerEventLoop { impl run_blocking::BlockingEventLoop for DebuggerEventLoop {
type Target = DebuggerTarget; type Target = DebuggerTarget;
@ -35,17 +35,12 @@ impl run_blocking::BlockingEventLoop for DebuggerEventLoop {
return Ok(run_blocking::Event::IncomingData(byte)); return Ok(run_blocking::Event::IncomingData(byte));
} else { } else {
// try and wait for the stop reason // try and wait for the stop reason
let (lock, cvar) = &*target.stop_signal; if let Some(stop_reason) =
let stop_reason = lock.lock().unwrap(); target.wait_for_stop_reason_timeout(Duration::from_millis(10))
let (stop_reason, timeout_result) = cvar {
.wait_timeout(stop_reason, Duration::from_millis(10)) info!("Target stopped due to {:?}!", stop_reason);
.unwrap(); return Ok(run_blocking::Event::TargetStopped(stop_reason));
if timeout_result.timed_out() {
// timed-out, try again later
continue;
} }
info!("Target stopped due to {:?}!", stop_reason);
return Ok(run_blocking::Event::TargetStopped(*stop_reason));
} }
} }
} }
@ -54,12 +49,8 @@ impl run_blocking::BlockingEventLoop for DebuggerEventLoop {
target: &mut DebuggerTarget, target: &mut DebuggerTarget,
) -> Result<Option<SingleThreadStopReason<u32>>, <DebuggerTarget as Target>::Error> { ) -> Result<Option<SingleThreadStopReason<u32>>, <DebuggerTarget as Target>::Error> {
info!("on_interrupt: sending stop message"); info!("on_interrupt: sending stop message");
target.tx.send(DebuggerRequest::Interrupt).unwrap(); target.debugger_request(DebuggerRequest::Interrupt);
target.wait_for_operation();
info!("Waiting for target to stop <blocking>"); info!("Waiting for target to stop <blocking>");
let (lock, cvar) = &*target.stop_signal; Ok(Some(target.wait_for_stop_reason_blocking()))
let stop_signal = lock.lock().unwrap();
let stop_signal = cvar.wait(stop_signal).unwrap();
Ok(Some(*stop_signal))
} }
} }

View file

@ -11,9 +11,8 @@ use arm7tdmi::{
use crate::{GBAError, GameBoyAdvance}; use crate::{GBAError, GameBoyAdvance};
use super::{ use super::target::DebuggerTarget;
event_loop::DebuggerEventLoop, DebuggerRequest, DebuggerRequestHandler, DebuggerTarget, use super::{event_loop::DebuggerEventLoop, DebuggerRequestHandler};
};
/// Starts a gdbserver thread /// Starts a gdbserver thread
pub(crate) fn start_gdb_server_thread( pub(crate) fn start_gdb_server_thread(
@ -22,10 +21,7 @@ pub(crate) fn start_gdb_server_thread(
) -> Result<DebuggerRequestHandler, GBAError> { ) -> Result<DebuggerRequestHandler, GBAError> {
let (tx, rx) = crossbeam::channel::unbounded(); let (tx, rx) = crossbeam::channel::unbounded();
let request_complete_signal = Arc::new((Mutex::new(false), Condvar::new())); let request_complete_signal = Arc::new((Mutex::new(false), Condvar::new()));
let stop_signal = Arc::new(( let stop_signal = Arc::new((Mutex::new(None), Condvar::new()));
Mutex::new(SingleThreadStopReason::Signal(Signal::SIGINT)),
Condvar::new(),
));
let stop_signal_2 = stop_signal.clone(); let stop_signal_2 = stop_signal.clone();
let request_complete_signal_2 = request_complete_signal.clone(); let request_complete_signal_2 = request_complete_signal.clone();
let memory_map = gba.sysbus.generate_memory_map_xml().unwrap(); let memory_map = gba.sysbus.generate_memory_map_xml().unwrap();
@ -35,21 +31,14 @@ pub(crate) fn start_gdb_server_thread(
debug!("starting GDB Server thread"); debug!("starting GDB Server thread");
let conn: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(conn); let conn: Box<dyn ConnectionExt<Error = std::io::Error>> = Box::new(conn);
let mut target = DebuggerTarget { let mut target =
tx, DebuggerTarget::new(tx, request_complete_signal_2, stop_signal_2, memory_map);
request_complete_signal: request_complete_signal_2,
stop_signal: stop_signal_2,
memory_map,
};
let gdbserver = GdbStub::new(conn); let gdbserver = GdbStub::new(conn);
let disconnect_reason = gdbserver let disconnect_reason = gdbserver
.run_blocking::<DebuggerEventLoop>(&mut target) .run_blocking::<DebuggerEventLoop>(&mut target)
.map_err(|e| e.to_string()) .map_err(|e| e.to_string())
.unwrap(); .unwrap();
target target.disconnect(disconnect_reason);
.tx
.send(DebuggerRequest::Disconnected(disconnect_reason))
.unwrap();
}); });
let mut debugger = DebuggerRequestHandler { let mut debugger = DebuggerRequestHandler {

View file

@ -1,8 +1,11 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
/// Implementing the Target trait for gdbstub /// Implementing the Target trait for gdbstub
use arm7tdmi::gdb::{copy_range_to_buf, gdbstub, gdbstub_arch}; use arm7tdmi::gdb::{copy_range_to_buf, gdbstub, gdbstub_arch};
use crossbeam::channel::Sender;
use gdbstub::common::Signal; use gdbstub::common::Signal;
use gdbstub::stub::{DisconnectReason, SingleThreadStopReason};
use gdbstub::target::ext::base::singlethread::{ use gdbstub::target::ext::base::singlethread::{
SingleThreadBase, SingleThreadResume, SingleThreadSingleStep, SingleThreadBase, SingleThreadResume, SingleThreadSingleStep,
}; };
@ -12,7 +15,78 @@ use gdbstub::target::ext::breakpoints::BreakpointsOps;
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;
use super::{DebuggerRequest, DebuggerTarget}; use super::DebuggerRequest;
pub(crate) struct DebuggerTarget {
tx: Sender<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
pub(crate) stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
pub(crate) memory_map: String,
}
impl DebuggerTarget {
pub fn new(
tx: Sender<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<Option<SingleThreadStopReason<u32>>>, Condvar)>,
memory_map: String,
) -> DebuggerTarget {
DebuggerTarget {
tx,
request_complete_signal,
stop_signal,
memory_map,
}
}
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();
}
*finished = false;
}
pub fn wait_for_stop_reason_timeout(
&mut self,
timeout: Duration,
) -> 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"))
}
}
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;
}
let mut stop_reason = cvar.wait(stop_reason).unwrap();
stop_reason.take().expect("None is not expected here")
}
pub fn disconnect(&mut self, disconnect_reason: DisconnectReason) {
self.tx
.send(DebuggerRequest::Disconnected(disconnect_reason))
.unwrap();
}
}
impl Target for DebuggerTarget { impl Target for DebuggerTarget {
type Error = (); type Error = ();
@ -37,28 +111,19 @@ impl Target for DebuggerTarget {
impl SingleThreadBase for DebuggerTarget { impl SingleThreadBase for DebuggerTarget {
fn read_registers(&mut self, regs: &mut ArmCoreRegs) -> TargetResult<(), Self> { fn read_registers(&mut self, regs: &mut ArmCoreRegs) -> TargetResult<(), Self> {
let regs_copy = Arc::new(Mutex::new(ArmCoreRegs::default())); let regs_copy = Arc::new(Mutex::new(ArmCoreRegs::default()));
self.tx self.debugger_request(DebuggerRequest::ReadRegs(regs_copy.clone()));
.send(DebuggerRequest::ReadRegs(regs_copy.clone()))
.unwrap();
self.wait_for_operation();
regs_copy.lock().unwrap().clone_into(regs); regs_copy.lock().unwrap().clone_into(regs);
Ok(()) Ok(())
} }
fn write_registers(&mut self, regs: &ArmCoreRegs) -> TargetResult<(), Self> { fn write_registers(&mut self, regs: &ArmCoreRegs) -> TargetResult<(), Self> {
self.tx self.debugger_request(DebuggerRequest::WriteRegs(regs.clone()));
.send(DebuggerRequest::WriteRegs(regs.clone()))
.unwrap();
self.wait_for_operation();
Ok(()) Ok(())
} }
fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> { fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
let buffer = Arc::new(Mutex::new(vec![0; data.len()].into_boxed_slice())); let buffer = Arc::new(Mutex::new(vec![0; data.len()].into_boxed_slice()));
self.tx self.debugger_request(DebuggerRequest::ReadAddrs(start_addr, buffer.clone()));
.send(DebuggerRequest::ReadAddrs(start_addr, buffer.clone()))
.unwrap();
self.wait_for_operation();
data.copy_from_slice(&buffer.lock().unwrap()); data.copy_from_slice(&buffer.lock().unwrap());
Ok(()) Ok(())
} }
@ -78,8 +143,7 @@ impl SingleThreadBase for DebuggerTarget {
impl SingleThreadResume for DebuggerTarget { impl SingleThreadResume for DebuggerTarget {
fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> { fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
self.tx.send(DebuggerRequest::Resume).unwrap(); self.debugger_request(DebuggerRequest::Resume);
self.wait_for_operation();
Ok(()) Ok(())
} }
@ -94,8 +158,7 @@ impl SingleThreadResume for DebuggerTarget {
impl SingleThreadSingleStep for DebuggerTarget { impl SingleThreadSingleStep for DebuggerTarget {
fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> { fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
self.tx.send(DebuggerRequest::SingleStep).unwrap(); self.debugger_request(DebuggerRequest::SingleStep);
self.wait_for_operation();
Ok(()) Ok(())
} }
} }
@ -132,10 +195,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
addr: u32, addr: u32,
_kind: gdbstub_arch::arm::ArmBreakpointKind, _kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> { ) -> TargetResult<bool, Self> {
self.tx self.debugger_request(DebuggerRequest::AddSwBreakpoint(addr));
.send(DebuggerRequest::AddSwBreakpoint(addr))
.unwrap();
self.wait_for_operation();
Ok(true) Ok(true)
} }
@ -144,10 +204,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
addr: u32, addr: u32,
_kind: gdbstub_arch::arm::ArmBreakpointKind, _kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> { ) -> TargetResult<bool, Self> {
self.tx self.debugger_request(DebuggerRequest::DelSwBreakpoint(addr));
.send(DebuggerRequest::DelSwBreakpoint(addr))
.unwrap();
self.wait_for_operation();
Ok(true) Ok(true)
} }
} }

View file

@ -22,7 +22,7 @@ use self::consts::*;
pub enum HaltState { pub enum HaltState {
Running, Running,
Halt, // In Halt mode, the CPU is paused as long as (IE AND IF)=0, Halt, // In Halt mode, the CPU is paused as long as (IE AND IF)=0,
Stop, // In Stop mode, most of the hardware including sound and video are paused // Stop, // In Stop mode, most of the hardware including sound and video are paused TODO: handle
} }
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
@ -288,7 +288,7 @@ impl BusIO for IoDevices {
REG_POSTFLG => io.post_boot_flag = value != 0, REG_POSTFLG => io.post_boot_flag = value != 0,
REG_HALTCNT => { REG_HALTCNT => {
if value & 0x80 != 0 { if value & 0x80 != 0 {
io.haltcnt = HaltState::Stop; // io.haltcnt = HaltState::Stop;
panic!("Can't handle HaltCtrl == Stop yet"); panic!("Can't handle HaltCtrl == Stop yet");
} else { } else {
io.haltcnt = HaltState::Halt; io.haltcnt = HaltState::Halt;

View file

@ -194,9 +194,12 @@ impl Scheduler {
} }
#[inline] #[inline]
/// The event queue is assumed to be not empty /// Safety - Onyl safe to call when we know the event queue is not empty
pub fn timestamp_of_next_event(&self) -> usize { pub unsafe fn timestamp_of_next_event_unchecked(&self) -> usize {
self.events.peek().unwrap_or_else(|| unreachable!()).time self.events
.peek()
.unwrap_or_else(|| std::hint::unreachable_unchecked())
.time
} }
#[inline] #[inline]

View file

@ -107,7 +107,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// normal_panic(panic_info); // normal_panic(panic_info);
// })); // }));
if opts.skip_bios { if opts.skip_bios {
println!("Skipping bios animation.."); println!("Skipping bios animation..");
gba.skip_bios(); gba.skip_bios();