Implement GDB/LLDB compatible server #3
|
@ -315,7 +315,7 @@ impl Emulator {
|
||||||
|
|
||||||
fn stop_debugging(&mut self, sim_id: SimId) {
|
fn stop_debugging(&mut self, sim_id: SimId) {
|
||||||
if let Some(sim) = self.sims.get_mut(sim_id.to_index()) {
|
if let Some(sim) = self.sims.get_mut(sim_id.to_index()) {
|
||||||
sim.clear_breakpoints();
|
sim.clear_debug_state();
|
||||||
}
|
}
|
||||||
self.debuggers.remove(&sim_id);
|
self.debuggers.remove(&sim_id);
|
||||||
if self.debuggers.is_empty() {
|
if self.debuggers.is_empty() {
|
||||||
|
@ -345,12 +345,21 @@ impl Emulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_continue(&mut self, sim_id: SimId) {
|
fn debug_continue(&mut self, sim_id: SimId) -> bool {
|
||||||
let Some(debugger) = self.debuggers.get_mut(&sim_id) else {
|
let Some(debugger) = self.debuggers.get_mut(&sim_id) else {
|
||||||
self.stop_debugging(sim_id);
|
self.stop_debugging(sim_id);
|
||||||
return;
|
return false;
|
||||||
};
|
};
|
||||||
debugger.stop_reason = None;
|
debugger.stop_reason = None;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn debug_step(&mut self, sim_id: SimId) {
|
||||||
|
if self.debug_continue(sim_id) {
|
||||||
|
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
sim.step();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
|
@ -412,6 +421,7 @@ impl Emulator {
|
||||||
};
|
};
|
||||||
if let Some(reason) = sim.stop_reason() {
|
if let Some(reason) = sim.stop_reason() {
|
||||||
let stop_reason = match reason {
|
let stop_reason = match reason {
|
||||||
|
StopReason::Stepped => DebugStopReason::Trace,
|
||||||
StopReason::Breakpoint => DebugStopReason::Breakpoint,
|
StopReason::Breakpoint => DebugStopReason::Breakpoint,
|
||||||
};
|
};
|
||||||
self.debug_stop(sim_id, stop_reason);
|
self.debug_stop(sim_id, stop_reason);
|
||||||
|
@ -509,6 +519,9 @@ impl Emulator {
|
||||||
EmulatorCommand::DebugContinue(sim_id) => {
|
EmulatorCommand::DebugContinue(sim_id) => {
|
||||||
self.debug_continue(sim_id);
|
self.debug_continue(sim_id);
|
||||||
}
|
}
|
||||||
|
EmulatorCommand::DebugStep(sim_id) => {
|
||||||
|
self.debug_step(sim_id);
|
||||||
|
}
|
||||||
EmulatorCommand::ReadRegister(sim_id, register, done) => {
|
EmulatorCommand::ReadRegister(sim_id, register, done) => {
|
||||||
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
|
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
|
||||||
return;
|
return;
|
||||||
|
@ -596,6 +609,7 @@ pub enum EmulatorCommand {
|
||||||
StopDebugging(SimId),
|
StopDebugging(SimId),
|
||||||
DebugInterrupt(SimId),
|
DebugInterrupt(SimId),
|
||||||
DebugContinue(SimId),
|
DebugContinue(SimId),
|
||||||
|
DebugStep(SimId),
|
||||||
ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
|
ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
|
||||||
ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
|
ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
|
||||||
AddBreakpoint(SimId, u32),
|
AddBreakpoint(SimId, u32),
|
||||||
|
@ -628,6 +642,8 @@ type DebugSender = tokio::sync::mpsc::UnboundedSender<DebugEvent>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum DebugStopReason {
|
pub enum DebugStopReason {
|
||||||
|
// We are stepping
|
||||||
|
Trace,
|
||||||
// We hit a breakpoint
|
// We hit a breakpoint
|
||||||
Breakpoint,
|
Breakpoint,
|
||||||
// The debugger told us to pause
|
// The debugger told us to pause
|
||||||
|
|
|
@ -147,12 +147,18 @@ extern "C" fn on_execute(sim: *mut VB, address: u32, _code: *const u16, _length:
|
||||||
// There is no way for the userdata to be null or otherwise invalid.
|
// There is no way for the userdata to be null or otherwise invalid.
|
||||||
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
|
let data: &mut VBState = unsafe { &mut *vb_get_user_data(sim).cast() };
|
||||||
|
|
||||||
if data.breakpoints.binary_search(&address).is_err() {
|
let mut stopped = 0;
|
||||||
return 0;
|
if data.step_from.is_some_and(|s| s != address) {
|
||||||
|
data.step_from = None;
|
||||||
|
data.stop_reason = Some(StopReason::Stepped);
|
||||||
|
stopped = 1;
|
||||||
|
}
|
||||||
|
if data.breakpoints.binary_search(&address).is_ok() {
|
||||||
|
data.stop_reason = Some(StopReason::Breakpoint);
|
||||||
|
stopped = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.stop_reason = Some(StopReason::Breakpoint);
|
stopped
|
||||||
1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
|
const AUDIO_CAPACITY_SAMPLES: usize = 834 * 4;
|
||||||
|
@ -162,6 +168,7 @@ pub const EXPECTED_FRAME_SIZE: usize = 834 * 2;
|
||||||
struct VBState {
|
struct VBState {
|
||||||
frame_seen: bool,
|
frame_seen: bool,
|
||||||
stop_reason: Option<StopReason>,
|
stop_reason: Option<StopReason>,
|
||||||
|
step_from: Option<u32>,
|
||||||
breakpoints: Vec<u32>,
|
breakpoints: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,6 +179,7 @@ pub struct Sim {
|
||||||
|
|
||||||
pub enum StopReason {
|
pub enum StopReason {
|
||||||
Breakpoint,
|
Breakpoint,
|
||||||
|
Stepped,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sim {
|
impl Sim {
|
||||||
|
@ -183,12 +191,14 @@ impl Sim {
|
||||||
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
|
let sim: *mut VB = Box::into_raw(memory.into_boxed_slice()).cast();
|
||||||
unsafe { vb_init(sim) };
|
unsafe { vb_init(sim) };
|
||||||
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) };
|
unsafe { vb_set_option(sim, VBOption::PseudoHalt, 1) };
|
||||||
|
unsafe { vb_set_keys(sim, VBKey::SGN.bits()) };
|
||||||
unsafe { vb_reset(sim) };
|
unsafe { vb_reset(sim) };
|
||||||
|
|
||||||
// set up userdata
|
// set up userdata
|
||||||
let state = VBState {
|
let state = VBState {
|
||||||
frame_seen: false,
|
frame_seen: false,
|
||||||
stop_reason: None,
|
stop_reason: None,
|
||||||
|
step_from: None,
|
||||||
breakpoints: vec![],
|
breakpoints: vec![],
|
||||||
};
|
};
|
||||||
unsafe { vb_set_user_data(sim, Box::into_raw(Box::new(state)).cast()) };
|
unsafe { vb_set_user_data(sim, Box::into_raw(Box::new(state)).cast()) };
|
||||||
|
@ -361,21 +371,35 @@ impl Sim {
|
||||||
let data = self.get_state();
|
let data = self.get_state();
|
||||||
if let Ok(index) = data.breakpoints.binary_search(&address) {
|
if let Ok(index) = data.breakpoints.binary_search(&address) {
|
||||||
data.breakpoints.remove(index);
|
data.breakpoints.remove(index);
|
||||||
if data.breakpoints.is_empty() {
|
if data.step_from.is_none() && data.breakpoints.is_empty() {
|
||||||
unsafe { vb_set_execute_callback(self.sim, None) };
|
unsafe { vb_set_execute_callback(self.sim, None) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_breakpoints(&mut self) {
|
pub fn step(&mut self) {
|
||||||
|
let current_pc = unsafe { vb_get_program_counter(self.sim) };
|
||||||
let data = self.get_state();
|
let data = self.get_state();
|
||||||
|
data.step_from = Some(current_pc);
|
||||||
|
unsafe {
|
||||||
|
vb_set_execute_callback(self.sim, Some(on_execute));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_debug_state(&mut self) {
|
||||||
|
let data = self.get_state();
|
||||||
|
data.step_from = None;
|
||||||
data.breakpoints.clear();
|
data.breakpoints.clear();
|
||||||
unsafe { vb_set_execute_callback(self.sim, None) };
|
unsafe { vb_set_execute_callback(self.sim, None) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop_reason(&mut self) -> Option<StopReason> {
|
pub fn stop_reason(&mut self) -> Option<StopReason> {
|
||||||
let data = self.get_state();
|
let data = self.get_state();
|
||||||
data.stop_reason.take()
|
let reason = data.stop_reason.take();
|
||||||
|
if data.step_from.is_none() && data.breakpoints.is_empty() {
|
||||||
|
unsafe { vb_set_execute_callback(self.sim, None) };
|
||||||
|
}
|
||||||
|
reason
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(&mut self) -> &mut VBState {
|
fn get_state(&mut self) -> &mut VBState {
|
||||||
|
|
|
@ -258,6 +258,12 @@ impl GdbConnection {
|
||||||
self.stop_reason = None;
|
self.stop_reason = None;
|
||||||
// Don't send a response until we hit a breakpoint or get interrupted
|
// Don't send a response until we hit a breakpoint or get interrupted
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
} else if req.match_str("s") {
|
||||||
|
self.client
|
||||||
|
.send_command(EmulatorCommand::DebugStep(self.sim_id));
|
||||||
|
self.stop_reason = None;
|
||||||
|
// Don't send a response until we hit a breakpoint or get interrupted
|
||||||
|
return Ok(None);
|
||||||
} else if req.match_str("p") {
|
} else if req.match_str("p") {
|
||||||
let mut read_register = || {
|
let mut read_register = || {
|
||||||
let register_index = req.match_hex::<usize>()?;
|
let register_index = req.match_hex::<usize>()?;
|
||||||
|
@ -348,7 +354,8 @@ impl Drop for GdbConnection {
|
||||||
|
|
||||||
fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> &'static str {
|
fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> &'static str {
|
||||||
match reason {
|
match reason {
|
||||||
Some(DebugStopReason::Breakpoint) => "T05;thread:p1.t1;threads;p1.t1;reason:breakpoint",
|
Some(DebugStopReason::Trace) => "T05;thread:p1.t1;threads:p1.t1;reason:trace;",
|
||||||
|
Some(DebugStopReason::Breakpoint) => "T05;thread:p1.t1;threads:p1.t1;reason:breakpoint;",
|
||||||
Some(DebugStopReason::Trapped) => "T05;swbreak;thread:p1.t1;threads:p1.t1;reason:trap;",
|
Some(DebugStopReason::Trapped) => "T05;swbreak;thread:p1.t1;threads:p1.t1;reason:trap;",
|
||||||
None => "T00;thread:p1.t1;threads:p1.t1;",
|
None => "T00;thread:p1.t1;threads:p1.t1;",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue