Gdb fixes

Former-commit-id: 2940580fc6b3760d77b5598b0faf72a773183304
Former-commit-id: cf3f361178c75d4e39832774bdd052ef8aab6be8
This commit is contained in:
Michel Heily 2022-09-19 00:10:55 +03:00
parent 3bb480c120
commit c8c1cdd57b
9 changed files with 205 additions and 130 deletions

View file

@ -1,11 +1,18 @@
use std::fmt;
use log::debug;
use serde::{Deserialize, Serialize};
use bit::BitIndex;
use num::FromPrimitive;
use ansi_term::{Colour, Style};
use rustboyadvance_utils::{Shared, WeakPointer};
pub use super::exception::Exception;
use super::reg_string;
use super::{arm::ArmCond, psr::RegPSR, Addr, CpuMode, CpuState};
use rustboyadvance_utils::{Shared, WeakPointer};
use super::memory::{MemoryAccess, MemoryInterface};
use MemoryAccess::*;
@ -37,18 +44,12 @@ cfg_if! {
use super::DecodedInstruction;
use super::arm::ArmInstruction;
use super::thumb::ThumbInstruction;
use super::reg_string;
use std::fmt;
use ansi_term::{Colour, Style};
} else {
}
}
use bit::BitIndex;
use num::FromPrimitive;
pub enum CpuAction {
AdvancePC(MemoryAccess),
PipelineFlushed,
@ -104,7 +105,7 @@ impl Default for DebuggerState {
}
}
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Arm7tdmiCore<I: MemoryInterface> {
pub pc: u32,
pub bus: Shared<I>,
@ -506,6 +507,29 @@ impl<I: MemoryInterface> Arm7tdmiCore<I> {
}
}
impl<I: MemoryInterface> fmt::Debug for Arm7tdmiCore<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ARM7TDMI Core Status:")?;
writeln!(f, "\tCPSR: {}", self.cpsr)?;
writeln!(f, "\tGeneral Purpose Registers:")?;
let reg_normal_style = Style::new().bold();
let gpr = self.copy_registers();
for i in 0..15 {
let mut reg_name = reg_string(i).to_string();
reg_name.make_ascii_uppercase();
let entry = format!("\t{:-3} = 0x{:08x}", reg_name, gpr[i]);
write!(
f,
"{}{}",
reg_normal_style.paint(entry),
if (i + 1) % 4 == 0 { "\n" } else { "" }
)?;
}
let pc = format!("\tPC = 0x{:08x}", self.get_next_pc());
writeln!(f, "{}", reg_normal_style.paint(pc))
}
}
#[cfg(feature = "debugger")]
impl<I: MemoryInterface> fmt::Display for Core<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

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.get_next_pc();
regs.pc = self.pc;
regs.lr = self.get_reg(REG_LR);
regs.sp = self.get_reg(REG_SP);
regs.r[..].copy_from_slice(&self.gpr[..13]);
@ -56,7 +56,7 @@ impl<I: MemoryGdbInterface> SingleThreadBase for Arm7tdmiCore<I> {
self.set_reg(REG_PC, regs.pc);
self.set_reg(REG_LR, regs.lr);
self.set_reg(REG_SP, regs.sp);
self.gpr.copy_from_slice(&regs.r);
self.gpr[..13].copy_from_slice(&regs.r);
self.cpsr.set(regs.cpsr);
Ok(())
}

View file

@ -6,7 +6,7 @@ use arm7tdmi::gdbstub::stub::SingleThreadStopReason;
use bincode;
use serde::{Deserialize, Serialize};
use crate::gdb_support::{gdb_thread::start_gdb_server_thread, DebuggerState};
use crate::gdb_support::{gdb_thread::start_gdb_server_thread, DebuggerRequestHandler};
use super::cartridge::Cartridge;
use super::dma::DmaController;
@ -24,13 +24,13 @@ use arm7tdmi::{self, Arm7tdmiCore};
use rustboyadvance_utils::Shared;
pub struct GameBoyAdvance {
pub(crate) cpu: Box<Arm7tdmiCore<SysBus>>,
pub cpu: Box<Arm7tdmiCore<SysBus>>,
pub(crate) sysbus: Shared<SysBus>,
pub(crate) io_devs: Shared<IoDevices>,
pub(crate) scheduler: SharedScheduler,
interrupt_flags: SharedInterruptFlags,
audio_interface: DynAudioInterface,
pub(crate) debugger: Option<DebuggerState>,
pub(crate) debugger: Option<DebuggerRequestHandler>,
}
#[derive(Serialize, Deserialize)]
@ -251,17 +251,12 @@ impl GameBoyAdvance {
/// Recv & handle messages from the debugger, and return if we are stopped or not
pub fn debugger_run(&mut self) {
let mut should_stop = false;
let mut should_interrupt_frame = false;
let debugger = self.debugger.take().expect("debugger should be None here");
let debugger = debugger
.handle_message(self, &mut should_stop)
.map_err(|_| "Failed to handle message")
.unwrap();
self.debugger = debugger;
self.debugger = debugger.handle_incoming_requests(self, &mut should_interrupt_frame);
if let Some(debugger) = &mut self.debugger {
if should_stop {
if should_interrupt_frame {
debugger.notify_stop_reason(SingleThreadStopReason::DoneStep);
} else {
self.frame_interruptible();
@ -277,7 +272,7 @@ impl GameBoyAdvance {
}
#[inline]
pub fn cpu_step(&mut self) {
pub(crate) fn cpu_step(&mut self) {
if self.io_devs.intc.irq_pending() {
self.cpu.irq();
self.io_devs.haltcnt = HaltState::Running;

View file

@ -1,9 +1,9 @@
use std::result;
use std::sync::{Arc, Condvar, Mutex};
use std::thread::JoinHandle;
type SendSync<T> = Arc<Mutex<T>>;
use arm7tdmi::gdbstub::common::Signal;
use arm7tdmi::gdbstub::stub::{DisconnectReason, SingleThreadStopReason};
use arm7tdmi::gdbstub::target::TargetError;
use arm7tdmi::gdbstub::target::{ext::base::singlethread::SingleThreadBase, Target};
@ -20,7 +20,7 @@ mod target;
use crate::GameBoyAdvance;
#[derive(Debug)]
pub(crate) enum DebuggerMessage {
pub(crate) enum DebuggerRequest {
ReadRegs(SendSync<ArmCoreRegs>),
WriteRegs(ArmCoreRegs),
ReadAddrs(Addr, SendSync<Box<[u8]>>),
@ -28,15 +28,15 @@ pub(crate) enum DebuggerMessage {
WriteAddrs(Addr, Box<[u8]>),
AddSwBreakpoint(Addr),
DelSwBreakpoint(Addr),
Stop,
Interrupt,
Resume,
SingleStep,
Disconnected(DisconnectReason),
}
pub struct DebuggerTarget {
tx: Sender<DebuggerMessage>,
operation_signal: Arc<(Mutex<bool>, Condvar)>,
tx: Sender<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, Condvar)>,
memory_map: String,
}
@ -44,7 +44,7 @@ pub struct DebuggerTarget {
impl DebuggerTarget {
#[inline]
pub fn wait_for_operation(&mut self) {
let (lock, cvar) = &*self.operation_signal;
let (lock, cvar) = &*self.request_complete_signal;
let mut finished = lock.lock().unwrap();
while !*finished {
finished = cvar.wait(finished).unwrap();
@ -53,39 +53,39 @@ impl DebuggerTarget {
}
}
pub(crate) struct DebuggerState {
rx: Receiver<DebuggerMessage>,
operation_signal: Arc<(Mutex<bool>, Condvar)>,
pub(crate) struct DebuggerRequestHandler {
rx: Receiver<DebuggerRequest>,
request_complete_signal: Arc<(Mutex<bool>, Condvar)>,
stop_signal: Arc<(Mutex<SingleThreadStopReason<u32>>, Condvar)>,
thread: JoinHandle<()>,
stopped: bool,
}
impl DebuggerState {
pub fn handle_message(
mut self,
gba: &mut GameBoyAdvance,
should_stop: &mut bool,
) -> Result<Option<DebuggerState>, TargetError<<DebuggerTarget as Target>::Error>> {
if self.thread.is_finished() {
warn!("gdb server thread unexpectdly died");
*should_stop = true;
self.thread.join().unwrap();
return Ok(None);
enum DebuggerStatus {
RequestComplete,
ResumeRequested,
StopRequested,
Disconnected(DisconnectReason),
}
if let Ok(msg) = self.rx.try_recv() {
use DebuggerMessage::*;
let mut result = match msg {
impl DebuggerRequestHandler {
fn handle_request(
&mut self,
gba: &mut GameBoyAdvance,
req: &mut DebuggerRequest,
) -> Result<DebuggerStatus, TargetError<<DebuggerTarget as Target>::Error>> {
use DebuggerRequest::*;
match req {
ReadRegs(regs) => {
let mut regs = regs.lock().unwrap();
gba.cpu.read_registers(&mut regs)?;
debug!("Debugger requested to read regs: {:?}", regs);
Ok(Some(self))
Ok(DebuggerStatus::RequestComplete)
}
WriteRegs(regs) => {
debug!("Debugger requested to write regs: {:?}", regs);
gba.cpu.write_registers(&regs)?;
Ok(Some(self))
gba.cpu.write_registers(regs)?;
Ok(DebuggerStatus::RequestComplete)
}
ReadAddrs(addr, data) => {
let mut data = data.lock().unwrap();
@ -94,8 +94,8 @@ impl DebuggerState {
data.len(),
addr
);
gba.cpu.read_addrs(addr, &mut data)?;
Ok(Some(self))
gba.cpu.read_addrs(*addr, &mut data)?;
Ok(DebuggerStatus::RequestComplete)
}
WriteAddrs(addr, data) => {
debug!(
@ -103,55 +103,89 @@ impl DebuggerState {
data.len(),
addr
);
gba.cpu.write_addrs(addr, &data)?;
Ok(Some(self))
gba.cpu.write_addrs(*addr, &data)?;
Ok(DebuggerStatus::RequestComplete)
}
Stop => {
Interrupt => {
debug!("Debugger requested stopped");
self.stopped = true;
Ok(Some(self))
self.notify_stop_reason(SingleThreadStopReason::Signal(Signal::SIGINT));
Ok(DebuggerStatus::StopRequested)
}
Resume => {
debug!("Debugger requested resume");
self.stopped = false;
Ok(Some(self))
Ok(DebuggerStatus::ResumeRequested)
}
SingleStep => {
debug!("Debugger requested single step");
gba.run::<true>(1);
self.notify_stop_reason(SingleThreadStopReason::DoneStep);
self.stopped = true;
Ok(Some(self))
gba.cpu_step();
let stop_reason = SingleThreadStopReason::DoneStep;
self.notify_stop_reason(stop_reason);
Ok(DebuggerStatus::StopRequested)
}
AddSwBreakpoint(addr) => {
gba.cpu.add_breakpoint(addr);
Ok(Some(self))
gba.cpu.add_breakpoint(*addr);
Ok(DebuggerStatus::RequestComplete)
}
DelSwBreakpoint(addr) => {
gba.cpu.del_breakpoint(addr);
Ok(Some(self))
gba.cpu.del_breakpoint(*addr);
Ok(DebuggerStatus::RequestComplete)
}
Disconnected(reason) => {
Disconnected(reason) => Ok(DebuggerStatus::Disconnected(*reason)),
}
}
fn terminate(mut self, should_interrupt_frame: &mut bool) -> Option<DebuggerRequestHandler> {
self.notify_stop_reason(SingleThreadStopReason::Exited(1));
self.thread.join().unwrap();
self.stopped = true;
*should_interrupt_frame = true;
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");
self.thread.join().unwrap();
Ok(None)
return self.terminate(should_interrupt_frame);
}
};
if let Ok(Some(result)) = &mut result {
let (lock, cvar) = &*result.operation_signal;
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) {
let (lock, cvar) = &*self.request_complete_signal;
let mut finished = lock.lock().unwrap();
*finished = true;
cvar.notify_one();
*should_stop = result.stopped;
} else {
*should_stop = true;
}
result
} else {
*should_stop = self.stopped;
Ok(Some(self))
}
}
pub fn notify_stop_reason(&mut self, reason: SingleThreadStopReason<u32>) {

View file

@ -6,7 +6,7 @@ use arm7tdmi::gdb::gdbstub::{
target::Target,
};
use super::{DebuggerMessage, DebuggerTarget};
use super::{DebuggerRequest, DebuggerTarget};
pub struct DebuggerEventLoop {}
@ -54,7 +54,7 @@ impl run_blocking::BlockingEventLoop for DebuggerEventLoop {
target: &mut DebuggerTarget,
) -> Result<Option<SingleThreadStopReason<u32>>, <DebuggerTarget as Target>::Error> {
info!("on_interrupt: sending stop message");
target.tx.send(DebuggerMessage::Stop).unwrap();
target.tx.send(DebuggerRequest::Interrupt).unwrap();
target.wait_for_operation();
info!("Waiting for target to stop <blocking>");
let (lock, cvar) = &*target.stop_signal;

View file

@ -11,21 +11,23 @@ use arm7tdmi::{
use crate::{GBAError, GameBoyAdvance};
use super::{event_loop::DebuggerEventLoop, DebuggerMessage, DebuggerState, DebuggerTarget};
use super::{
event_loop::DebuggerEventLoop, DebuggerRequest, DebuggerRequestHandler, DebuggerTarget,
};
/// Starts a gdbserver thread
pub(crate) fn start_gdb_server_thread(
gba: &mut GameBoyAdvance,
port: u16,
) -> Result<DebuggerState, GBAError> {
) -> Result<DebuggerRequestHandler, GBAError> {
let (tx, rx) = crossbeam::channel::unbounded();
let operation_signal = Arc::new((Mutex::new(false), Condvar::new()));
let request_complete_signal = Arc::new((Mutex::new(false), Condvar::new()));
let stop_signal = Arc::new((
Mutex::new(SingleThreadStopReason::Signal(Signal::SIGINT)),
Condvar::new(),
));
let stop_signal_2 = stop_signal.clone();
let operation_signal_2 = operation_signal.clone();
let request_complete_signal_2 = request_complete_signal.clone();
let memory_map = gba.sysbus.generate_memory_map_xml().unwrap();
let conn = wait_for_connection(port)?;
@ -35,7 +37,7 @@ pub(crate) fn start_gdb_server_thread(
let mut target = DebuggerTarget {
tx,
operation_signal: operation_signal_2,
request_complete_signal: request_complete_signal_2,
stop_signal: stop_signal_2,
memory_map,
};
@ -46,13 +48,13 @@ pub(crate) fn start_gdb_server_thread(
.unwrap();
target
.tx
.send(DebuggerMessage::Disconnected(disconnect_reason))
.send(DebuggerRequest::Disconnected(disconnect_reason))
.unwrap();
});
let mut debugger = DebuggerState {
let mut debugger = DebuggerRequestHandler {
rx,
operation_signal,
request_complete_signal,
stop_signal,
thread,
stopped: true,

View file

@ -12,7 +12,7 @@ use gdbstub::target::ext::breakpoints::BreakpointsOps;
use gdbstub::target::{self, Target, TargetError, TargetResult};
use gdbstub_arch::arm::reg::ArmCoreRegs;
use super::{DebuggerMessage, DebuggerTarget};
use super::{DebuggerRequest, DebuggerTarget};
impl Target for DebuggerTarget {
type Error = ();
@ -38,7 +38,7 @@ impl SingleThreadBase for DebuggerTarget {
fn read_registers(&mut self, regs: &mut ArmCoreRegs) -> TargetResult<(), Self> {
let regs_copy = Arc::new(Mutex::new(ArmCoreRegs::default()));
self.tx
.send(DebuggerMessage::ReadRegs(regs_copy.clone()))
.send(DebuggerRequest::ReadRegs(regs_copy.clone()))
.unwrap();
self.wait_for_operation();
regs_copy.lock().unwrap().clone_into(regs);
@ -47,7 +47,7 @@ impl SingleThreadBase for DebuggerTarget {
fn write_registers(&mut self, regs: &ArmCoreRegs) -> TargetResult<(), Self> {
self.tx
.send(DebuggerMessage::WriteRegs(regs.clone()))
.send(DebuggerRequest::WriteRegs(regs.clone()))
.unwrap();
self.wait_for_operation();
Ok(())
@ -56,7 +56,7 @@ impl SingleThreadBase for DebuggerTarget {
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()));
self.tx
.send(DebuggerMessage::ReadAddrs(start_addr, buffer.clone()))
.send(DebuggerRequest::ReadAddrs(start_addr, buffer.clone()))
.unwrap();
self.wait_for_operation();
data.copy_from_slice(&buffer.lock().unwrap());
@ -78,7 +78,7 @@ impl SingleThreadBase for DebuggerTarget {
impl SingleThreadResume for DebuggerTarget {
fn resume(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
self.tx.send(DebuggerMessage::Resume).unwrap();
self.tx.send(DebuggerRequest::Resume).unwrap();
self.wait_for_operation();
Ok(())
}
@ -94,9 +94,7 @@ impl SingleThreadResume for DebuggerTarget {
impl SingleThreadSingleStep for DebuggerTarget {
fn step(&mut self, _signal: Option<Signal>) -> Result<(), Self::Error> {
self.tx.send(DebuggerMessage::SingleStep).unwrap();
self.wait_for_operation();
self.tx.send(DebuggerMessage::Stop).unwrap();
self.tx.send(DebuggerRequest::SingleStep).unwrap();
self.wait_for_operation();
Ok(())
}
@ -135,7 +133,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
_kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> {
self.tx
.send(DebuggerMessage::AddSwBreakpoint(addr))
.send(DebuggerRequest::AddSwBreakpoint(addr))
.unwrap();
self.wait_for_operation();
Ok(true)
@ -147,7 +145,7 @@ impl target::ext::breakpoints::SwBreakpoint for DebuggerTarget {
_kind: gdbstub_arch::arm::ArmBreakpointKind,
) -> TargetResult<bool, Self> {
self.tx
.send(DebuggerMessage::DelSwBreakpoint(addr))
.send(DebuggerRequest::DelSwBreakpoint(addr))
.unwrap();
self.wait_for_operation();
Ok(true)

View file

@ -95,12 +95,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
audio_interface,
));
// let gba_raw_ptr = Box::into_raw(gba) as usize;
// static mut gba_raw: usize = 0;
// unsafe { gba_raw = gba_raw_ptr };
// let mut gba = unsafe {Box::from_raw(gba_raw_ptr as *mut GameBoyAdvance) };
// std::panic::set_hook(Box::new(|panic_info| {
// let gba = unsafe {Box::from_raw(gba_raw as *mut GameBoyAdvance) };
// println!("System crashed Oh No!!! {:?}", gba.cpu);
// let normal_panic = std::panic::take_hook();
// normal_panic(panic_info);
// }));
if opts.skip_bios {
println!("Skipping bios animation..");
gba.skip_bios();
}
if opts.gdbserver {
todo!("gdb")
gba.start_gdbserver(DEFAULT_GDB_SERVER_PORT);
}
let mut vsync = true;
@ -146,7 +160,10 @@ 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());
gba.restore_state(&save)?;
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)?);
info!("Restored!");
} else {
info!("Savestate not created, please create one by pressing F5");

View file

@ -1,9 +1,10 @@
use std::path::PathBuf;
use std::{path::PathBuf, io};
use rustboyadvance_core::{
cartridge::{BackupType, GamepakBuilder},
prelude::Cartridge,
};
use rustboyadvance_utils::read_bin_file;
use structopt::StructOpt;
const SAVE_TYPE_POSSIBLE_VALUES: &[&str] =
@ -61,4 +62,8 @@ impl Options {
pub fn rom_name(&self) -> &str {
self.rom.file_name().unwrap().to_str().unwrap()
}
pub fn read_rom(&self) -> Result<Vec<u8>, std::io::Error> {
read_bin_file(&self.rom)
}
}