debugger: Few improvements

This commit is contained in:
Michel Heily 2019-06-27 12:55:28 +03:00
parent 6552329310
commit b9d1d38c2d
2 changed files with 189 additions and 127 deletions

107
src/debugger/command.rs Normal file
View file

@ -0,0 +1,107 @@
use crate::debugger::Debugger;
use crate::disass::Disassembler;
use colored::*;
use hexdump;
#[derive(Debug, PartialEq, Clone)]
pub enum Command {
Info,
SingleStep,
Continue,
HexDump(u32, usize),
Disass(u32, usize),
AddBreakpoint(u32),
DelBreakpoint(u32),
ClearBreakpoints,
ListBreakpoints,
Reset,
Quit,
}
impl Command {
pub fn run(&self, debugger: &mut Debugger) {
use Command::*;
match *self {
Info => println!("{}", debugger.cpu),
SingleStep => {
if let Some(bp) = debugger.check_breakpoint() {
println!("breakpoint #{} reached!", bp);
debugger.delete_breakpoint(bp);
} else {
match debugger.cpu.step(&mut debugger.sysbus) {
Ok(_) => (),
Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu)
}
}
}
}
Continue => loop {
if let Some(bp) = debugger.check_breakpoint() {
println!("breakpoint #{} reached!", bp);
debugger.delete_breakpoint(bp);
break;
}
match debugger.cpu.step(&mut debugger.sysbus) {
Ok(_) => (),
Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu);
break;
}
};
},
HexDump(addr, nbytes) => {
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
Some(bytes) => bytes,
None => {
println!("requested content out of bounds");
return;
}
};
hexdump::hexdump(bytes);
}
Disass(addr, n) => {
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
Some(bytes) => bytes,
None => {
println!("requested content out of bounds");
return;
}
};
let disass = Disassembler::new(addr, bytes);
for line in disass {
println!("{}", line)
}
}
Quit => {
print!("Quitting!");
debugger.stop();
}
AddBreakpoint(addr) => {
if !debugger.breakpoints.contains(&addr) {
let new_index = debugger.breakpoints.len();
debugger.breakpoints.push(addr);
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
} else {
println!("breakpoint already exists!")
}
}
DelBreakpoint(addr) => debugger.delete_breakpoint(addr),
ClearBreakpoints => debugger.breakpoints.clear(),
ListBreakpoints => {
println!("breakpoint list:");
for (i, b) in debugger.breakpoints.iter().enumerate() {
println!("[{}] 0x{:08x}", i, b)
}
}
Reset => {
println!("resetting cpu...");
debugger.cpu.reset();
println!("cpu is restarted!")
}
}
}
}

View file

@ -3,16 +3,15 @@ use rustyline::Editor;
use colored::*; use colored::*;
use hexdump;
use super::arm7tdmi::cpu; use super::arm7tdmi::cpu;
use super::sysbus::SysBus; use super::sysbus::SysBus;
use crate::disass::Disassembler;
mod parser; mod parser;
use parser::{parse_expr, Expr, Value}; use parser::{parse_expr, Expr, Value};
mod command;
use command::Command;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DebuggerError { pub enum DebuggerError {
ParsingError(String), ParsingError(String),
@ -30,107 +29,13 @@ impl From<cpu::CpuError> for DebuggerError {
type DebuggerResult<T> = Result<T, DebuggerError>; type DebuggerResult<T> = Result<T, DebuggerError>;
#[derive(Debug, PartialEq, Clone)]
pub enum Command {
Info,
SingleStep,
Continue,
HexDump(u32, usize),
Disass(u32, usize),
AddBreakpoint(u32),
ListBreakpoints,
Reset,
Quit,
}
impl Command {
fn run(&self, debugger: &mut Debugger) {
use Command::*;
match *self {
Info => println!("{}", debugger.cpu),
SingleStep => {
match debugger.cpu.step(&mut debugger.sysbus) {
Ok(_) => (),
Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu);
}
};
if debugger.is_breakpoint_reached() {
println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc)
}
}
Continue => loop {
match debugger.cpu.step(&mut debugger.sysbus) {
Ok(_) => (),
Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu);
break;
}
};
if debugger.is_breakpoint_reached() {
println!("breakpoint 0x{:08x} reached!", debugger.cpu.pc)
}
},
HexDump(addr, nbytes) => {
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
Some(bytes) => bytes,
None => {
println!("requested content out of bounds");
return;
}
};
hexdump::hexdump(bytes);
}
Disass(addr, n) => {
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
Some(bytes) => bytes,
None => {
println!("requested content out of bounds");
return;
}
};
let disass = Disassembler::new(addr, bytes);
for line in disass {
println!("{}", line)
}
}
Quit => {
print!("Quitting!");
debugger.stop();
}
AddBreakpoint(addr) => {
if !debugger.breakpoints.contains(&addr) {
let new_index = debugger.breakpoints.len();
debugger.breakpoints.push(addr);
println!("added breakpoint [{}] 0x{:08x}", new_index, addr);
} else {
println!("breakpoint already exists!")
}
}
ListBreakpoints => {
println!("breakpoint list:");
for (i, b) in debugger.breakpoints.iter().enumerate() {
println!("[{}] 0x{:08x}", i, b)
}
}
Reset => {
println!("resetting cpu...");
debugger.cpu.reset();
println!("cpu is restarted!")
}
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Debugger { pub struct Debugger {
cpu: cpu::Core, pub cpu: cpu::Core,
sysbus: SysBus, pub sysbus: SysBus,
running: bool, running: bool,
breakpoints: Vec<u32>, breakpoints: Vec<u32>,
previous_command: Option<Command>, pub previous_command: Option<Command>,
} }
impl Debugger { impl Debugger {
@ -140,46 +45,57 @@ impl Debugger {
sysbus: sysbus, sysbus: sysbus,
breakpoints: Vec::new(), breakpoints: Vec::new(),
running: false, running: false,
previous_command: None previous_command: None,
} }
} }
fn is_breakpoint_reached(&self) -> bool { pub fn check_breakpoint(&self) -> Option<u32> {
let pc = self.cpu.pc; let pc = self.cpu.get_reg(15);
for b in &self.breakpoints { for bp in &self.breakpoints {
if *b == pc { if *bp == pc {
return true; return Some(pc);
} }
} }
false None
} }
pub fn delete_breakpoint(&mut self, addr: u32) {
self.breakpoints.retain(|&a| a != addr);
}
fn decode_reg(&self, s: &str) -> DebuggerResult<usize> { fn decode_reg(&self, s: &str) -> DebuggerResult<usize> {
let reg_names = vec![ let reg_names = vec![
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", "lr", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp",
"pc"]; "lr", "pc",
];
match reg_names.into_iter().position(|r| r == s) { match reg_names.into_iter().position(|r| r == s) {
Some(index) => Ok(index), Some(index) => Ok(index),
None => Err(DebuggerError::InvalidArgument(format!("{:?} is not a register name", s))) None => Err(DebuggerError::InvalidArgument(format!(
"{:?} is not a register name",
s
))),
} }
} }
fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> { fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> {
match arg { match arg {
Value::Name(reg) => { Value::Name(reg) => self.decode_reg(&reg),
self.decode_reg(&reg) v => Err(DebuggerError::InvalidArgument(format!(
}, "expected a number, got {:?}",
v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v))) v
))),
} }
} }
fn val_number(&self, arg: &Value) -> DebuggerResult<u32> { fn val_number(&self, arg: &Value) -> DebuggerResult<u32> {
match arg { match arg {
Value::Num(n) => Ok(*n), Value::Num(n) => Ok(*n),
v => Err(DebuggerError::InvalidArgument(format!("expected a number, got {:?}", v))) v => Err(DebuggerError::InvalidArgument(format!(
"expected a number, got {:?}",
v
))),
} }
} }
@ -190,7 +106,10 @@ impl Debugger {
let reg = self.decode_reg(&reg)?; let reg = self.decode_reg(&reg)?;
Ok(self.cpu.get_reg(reg)) Ok(self.cpu.get_reg(reg))
} }
v => Err(DebuggerError::InvalidArgument(format!("addr: expected a number or register, got {:?}", v))) v => Err(DebuggerError::InvalidArgument(format!(
"addr: expected a number or register, got {:?}",
v
))),
} }
} }
@ -206,10 +125,33 @@ impl Debugger {
"i" | "info" => Ok(Command::Info), "i" | "info" => Ok(Command::Info),
"s" | "step" => Ok(Command::SingleStep), "s" | "step" => Ok(Command::SingleStep),
"c" | "continue" => Ok(Command::Continue), "c" | "continue" => Ok(Command::Continue),
"xxd" => { "x" | "hexdump" => {
let (addr, n) = match args.len() {
2 => {
let addr = self.val_address(&args[0])?; let addr = self.val_address(&args[0])?;
let nbytes = self.val_number(&args[1])?; let n = self.val_number(&args[1])?;
Ok(Command::HexDump(addr, nbytes as usize))
(addr, n as usize)
}
1 => {
let addr = self.val_address(&args[0])?;
(addr, 0x100)
}
0 => {
if let Some(Command::HexDump(addr, n)) = self.previous_command {
(addr + (4 * n as u32), 0x100)
} else {
(self.cpu.get_reg(15), 0x100)
}
}
_ => {
return Err(DebuggerError::InvalidCommandFormat(
"xxd [addr] [n]".to_string(),
))
}
};
Ok(Command::HexDump(addr, n))
} }
"d" | "disass" => { "d" | "disass" => {
let (addr, n) = match args.len() { let (addr, n) = match args.len() {
@ -243,13 +185,23 @@ impl Debugger {
"b" | "break" => { "b" | "break" => {
if args.len() != 1 { if args.len() != 1 {
Err(DebuggerError::InvalidCommandFormat( Err(DebuggerError::InvalidCommandFormat(
"disass <addr>".to_string(), "break <addr>".to_string(),
)) ))
} else { } else {
let addr = self.val_address(&args[0])?; let addr = self.val_address(&args[0])?;
Ok(Command::AddBreakpoint(addr)) Ok(Command::AddBreakpoint(addr))
} }
} }
"bd" | "breakdel" => match args.len() {
0 => Ok(Command::ClearBreakpoints),
1 => {
let addr = self.val_address(&args[0])?;
Ok(Command::DelBreakpoint(addr))
}
_ => Err(DebuggerError::InvalidCommandFormat(String::from(
"breakdel [addr]",
))),
},
"bl" => Ok(Command::ListBreakpoints), "bl" => Ok(Command::ListBreakpoints),
"q" | "quit" => Ok(Command::Quit), "q" | "quit" => Ok(Command::Quit),
"r" | "reset" => Ok(Command::Reset), "r" | "reset" => Ok(Command::Reset),
@ -271,7 +223,7 @@ impl Debugger {
Ok(cmd) => { Ok(cmd) => {
self.previous_command = Some(cmd.clone()); self.previous_command = Some(cmd.clone());
cmd.run(self) cmd.run(self)
}, }
Err(DebuggerError::InvalidCommand(c)) => { Err(DebuggerError::InvalidCommand(c)) => {
println!("{}: {:?}", "invalid command".red(), c) println!("{}: {:?}", "invalid command".red(), c)
} }
@ -284,9 +236,11 @@ impl Debugger {
Err(e) => println!("{} {:?}", "failed to build command".red(), e), Err(e) => println!("{} {:?}", "failed to build command".red(), e),
}, },
Expr::Assignment(lvalue, rvalue) => match self.eval_assignment(lvalue, rvalue) { Expr::Assignment(lvalue, rvalue) => match self.eval_assignment(lvalue, rvalue) {
Err(DebuggerError::InvalidArgument(m)) => println!("{}: {}", "assignment error".red(), m), Err(DebuggerError::InvalidArgument(m)) => {
_ => () println!("{}: {}", "assignment error".red(), m)
} }
_ => (),
},
Expr::Empty => println!("Got empty expr"), Expr::Empty => println!("Got empty expr"),
} }
} }
@ -296,6 +250,7 @@ impl Debugger {
} }
pub fn repl(&mut self) -> DebuggerResult<()> { pub fn repl(&mut self) -> DebuggerResult<()> {
println!("Welcome to rustboyadvance-NG debugger 😎!\n");
self.running = true; self.running = true;
let mut rl = Editor::<()>::new(); let mut rl = Editor::<()>::new();
rl.load_history(".rustboyadvance_history"); rl.load_history(".rustboyadvance_history");