use gdb_protocol::io::BUF_SIZE;
use log::{debug, info, trace};
use nom::IResult::*;
use nom::{
alt, alt_complete, call, complete, do_parse, error_position, flat_map, is_not, is_not_s, many0,
many1, map, map_res, named, one_of, opt, preceded, separated_list, separated_list_complete,
separated_nonempty_list, separated_nonempty_list_complete, separated_pair, tag, take,
take_till, take_while1, try_parse, tuple, tuple_parser,
};
use nom::{IResult, Needed};
use rustc_serialize::hex::ToHex;
use std::borrow::Cow;
use std::convert::From;
use std::ops::Range;
use std::str::{self, FromStr};
use strum_macros::EnumString;
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, EnumString, PartialEq)]
enum GDBFeature {
multiprocess,
xmlRegisters,
qRelocInsn,
swbreak,
hwbreak,
#[strum(serialize = "fork-events")]
fork_events,
#[strum(serialize = "vfork-events")]
vfork_events,
#[strum(serialize = "exec-events")]
exec_events,
vContSupported,
#[strum(serialize = "no-resumed")]
no_resumed,
QThreadEvents,
}
#[derive(Clone, Debug, PartialEq)]
enum Known<'a> {
Yes(GDBFeature),
No(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
struct GDBFeatureSupported<'a>(Known<'a>, FeatureSupported<'a>);
#[derive(Clone, Debug, PartialEq)]
enum FeatureSupported<'a> {
Yes,
No,
#[allow(unused)]
Maybe,
Value(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
enum Query<'a> {
Attached(Option<u64>),
CurrentThread,
SearchMemory {
address: u64,
length: u64,
bytes: Vec<u8>,
},
SupportedFeatures(Vec<GDBFeatureSupported<'a>>),
StartNoAckMode,
Invoke(Vec<u8>),
AddressRandomization(bool),
CatchSyscalls(Option<Vec<u64>>),
PassSignals(Vec<u64>),
ProgramSignals(Vec<u64>),
ThreadInfo(ThreadId),
ThreadList(bool),
ProcessList(bool),
HostInfo,
FeatureRead {
name: String,
offset: u64,
length: u64,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Id {
Id(u32),
All,
Any,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ThreadId {
pub pid: Id,
pub tid: Id,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ProcessInfo {
pub name: String,
pub pid: Id,
pub triple: String,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Watchpoint {
pub addr: u64,
pub n_bytes: u64,
}
impl Watchpoint {
fn new(addr: u64, n_bytes: u64) -> Watchpoint {
Watchpoint { addr, n_bytes }
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Bytecode {
pub bytecode: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Breakpoint {
pub addr: u64,
pub kind: u64,
pub conditions: Option<Vec<Bytecode>>,
pub commands: Option<Vec<Bytecode>>,
}
impl Breakpoint {
fn new(
addr: u64,
kind: u64,
conditions: Option<Vec<Bytecode>>,
commands: Option<Vec<Bytecode>>,
) -> Breakpoint {
Breakpoint {
addr,
kind,
conditions,
commands,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct MemoryRegion {
pub address: u64,
pub length: u64,
}
impl MemoryRegion {
fn new(address: u64, length: u64) -> MemoryRegion {
MemoryRegion { address, length }
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VContFeature {
Continue = b'c',
ContinueWithSignal = b'C',
Step = b's',
StepWithSignal = b'S',
Stop = b't',
RangeStep = b'r',
}
#[derive(Clone, Debug, PartialEq)]
pub enum VCont {
Continue,
ContinueWithSignal(u8),
Step,
StepWithSignal(u8),
Stop,
RangeStep(Range<u64>),
}
#[derive(Clone, Debug, PartialEq)]
enum Command<'a> {
Detach(Option<u64>),
EnableExtendedMode,
TargetHaltReason,
ReadGeneralRegisters,
WriteGeneralRegisters(Vec<u8>),
ReadRegister(u64),
WriteRegister(u64, Vec<u8>),
Kill(Option<u64>),
ReadMemory(MemoryRegion),
WriteMemory(MemoryRegion, Vec<u8>),
Query(Query<'a>),
Reset,
PingThread(ThreadId),
CtrlC,
UnknownV,
SetCurrentThread(ThreadId),
InsertSoftwareBreakpoint(Breakpoint),
InsertHardwareBreakpoint(Breakpoint),
InsertWriteWatchpoint(Watchpoint),
InsertReadWatchpoint(Watchpoint),
InsertAccessWatchpoint(Watchpoint),
RemoveSoftwareBreakpoint(Breakpoint),
RemoveHardwareBreakpoint(Breakpoint),
RemoveWriteWatchpoint(Watchpoint),
RemoveReadWatchpoint(Watchpoint),
RemoveAccessWatchpoint(Watchpoint),
VContSupported,
VCont(Vec<(VCont, Option<ThreadId>)>),
}
named!(
gdbfeature<Known>,
map!(map_res!(is_not_s!(";="), str::from_utf8), |s| {
match GDBFeature::from_str(s) {
Ok(f) => Known::Yes(f),
Err(_) => Known::No(s),
}
})
);
fn gdbfeaturesupported<'a>(i: &'a [u8]) -> IResult<&'a [u8], GDBFeatureSupported<'a>> {
flat_map!(i, is_not!(";"), |f: &'a [u8]| {
match f.split_last() {
None => IResult::Incomplete(Needed::Size(2)),
Some((&b'+', first)) => map!(first, gdbfeature, |feat| GDBFeatureSupported(
feat,
FeatureSupported::Yes
)),
Some((&b'-', first)) => map!(first, gdbfeature, |feat| GDBFeatureSupported(
feat,
FeatureSupported::No
)),
Some((_, _)) => map!(
f,
separated_pair!(
gdbfeature,
tag!("="),
map_res!(is_not!(";"), str::from_utf8)
),
|(feat, value)| GDBFeatureSupported(feat, FeatureSupported::Value(value))
),
}
})
}
named!(q_search_memory<&[u8], (u64, u64, Vec<u8>)>,
complete!(do_parse!(
tag!("qSearch:memory:") >>
address: hex_value >>
tag!(";") >>
length: hex_value >>
tag!(";") >>
data: hex_byte_sequence >>
(address, length, data))));
named!(q_read_feature<&[u8], (&str, u64, u64)>,
complete!(do_parse!(
tag!("qXfer:features:read:") >>
filename: map!(is_not_s!(":"), |s| std::str::from_utf8(s).unwrap()) >>
tag!(":") >>
offset: hex_value >>
tag!(",") >>
length: hex_value >>
(filename, offset, length))));
fn query<'a>(i: &'a [u8]) -> IResult<&'a [u8], Query<'a>> {
alt_complete!(i,
tag!("qC") => { |_| Query::CurrentThread }
| preceded!(tag!("qSupported"),
preceded!(tag!(":"),
separated_list_complete!(tag!(";"),
gdbfeaturesupported))) => {
|features: Vec<GDBFeatureSupported<'a>>| Query::SupportedFeatures(features)
}
| q_read_feature => {
|(filename, offset, length): (&str, u64, u64)| Query::FeatureRead {
name: filename.to_string(),
offset, length,
}
}
| preceded!(tag!("qRcmd,"), hex_byte_sequence) => {
|bytes| Query::Invoke(bytes)
}
| q_search_memory => {
|(address, length, bytes)| Query::SearchMemory { address, length, bytes }
}
| tag!("QStartNoAckMode") => { |_| Query::StartNoAckMode }
| preceded!(tag!("qAttached:"), hex_value) => {
|value| Query::Attached(Some(value))
}
| tag!("qAttached") => { |_| Query::Attached(None) }
| tag!("qfThreadInfo") => { |_| Query::ThreadList(true) }
| tag!("qsThreadInfo") => { |_| Query::ThreadList(false) }
| tag!("QDisableRandomization:0") => { |_| Query::AddressRandomization(true) }
| tag!("QDisableRandomization:1") => { |_| Query::AddressRandomization(false) }
| tag!("QCatchSyscalls:0") => { |_| Query::CatchSyscalls(None) }
| preceded!(tag!("QCatchSyscalls:1"),
many0!(preceded!(tag!(";"), hex_value))) => {
|syscalls| Query::CatchSyscalls(Some(syscalls))
}
| preceded!(tag!("QPassSignals:"),
separated_list_complete!(tag!(";"), hex_value)) => {
|signals| Query::PassSignals(signals)
}
| preceded!(tag!("QProgramSignals:"),
separated_nonempty_list_complete!(tag!(";"), hex_value)) => {
|signals| Query::ProgramSignals(signals)
}
| preceded!(tag!("qThreadExtraInfo,"), parse_thread_id) => {
|thread_id| Query::ThreadInfo(thread_id)
}
| tag!("qfProcessInfo") => { |_| Query::ProcessList(true) }
| tag!("qsProcessInfo") => { |_| Query::ProcessList(false) }
| tag!("qHostInfo") => { |_| Query::HostInfo }
)
}
named!(hex_value<&[u8], u64>,
map!(take_while1!(&nom::is_hex_digit),
|hex| {
let s = str::from_utf8(hex).unwrap();
let r = u64::from_str_radix(s, 16);
r.unwrap()
}));
named!(hex_digit<&[u8], char>,
one_of!("0123456789abcdefABCDEF"));
named!(hex_byte<&[u8], u8>,
do_parse!(
digit0: hex_digit >>
digit1: hex_digit >>
((16 * digit0.to_digit(16).unwrap() + digit1.to_digit(16).unwrap()) as u8)
)
);
named!(hex_byte_sequence<&[u8], Vec<u8>>,
many1!(hex_byte));
named!(write_memory<&[u8], (u64, u64, Vec<u8>)>,
complete!(do_parse!(
tag!("M") >>
address: hex_value >>
tag!(",") >>
length: hex_value >>
tag!(":") >>
data: hex_byte_sequence >>
(address, length, data))));
named!(binary_byte<&[u8], u8>,
alt_complete!(
preceded!(tag!("}"), take!(1)) => { |b: &[u8]| b[0] ^ 0x20 } |
take!(1) => { |b: &[u8]| b[0] }));
named!(binary_byte_sequence<&[u8], Vec<u8>>,
many1!(binary_byte));
named!(write_memory_binary<&[u8], (u64, u64, Vec<u8>)>,
complete!(do_parse!(
tag!("X") >>
address: hex_value >>
tag!(",") >>
length: hex_value >>
tag!(":") >>
data: binary_byte_sequence >>
(address, length, data))));
named!(read_memory<&[u8], (u64, u64)>,
preceded!(tag!("m"),
separated_pair!(hex_value,
tag!(","),
hex_value)));
named!(read_register<&[u8], u64>,
preceded!(tag!("p"), hex_value));
named!(write_register<&[u8], (u64, Vec<u8>)>,
preceded!(tag!("P"),
separated_pair!(hex_value,
tag!("="),
hex_byte_sequence)));
named!(write_general_registers<&[u8], Vec<u8>>,
preceded!(tag!("G"), hex_byte_sequence));
named!(parse_thread_id_element<&[u8], Id>,
alt_complete!(tag!("0") => { |_| Id::Any }
| tag!("-1") => { |_| Id::All }
| hex_value => { |val: u64| Id::Id(val as u32) }));
named!(parse_thread_id<&[u8], ThreadId>,
alt_complete!(parse_thread_id_element => { |pid| ThreadId { pid, tid: Id::Any } }
| preceded!(tag!("p"),
separated_pair!(parse_thread_id_element,
tag!("."),
parse_thread_id_element)) => {
|pair: (Id, Id)| ThreadId { pid: pair.0, tid: pair.1 }
}
| preceded!(tag!("p"), parse_thread_id_element) => {
|id: Id| ThreadId { pid: id, tid: Id::All }
}));
named!(parse_ping_thread<&[u8], ThreadId>,
preceded!(tag!("T"), parse_thread_id));
fn v_command(i: &[u8]) -> IResult<&[u8], Command> {
alt_complete!(i,
tag!("vCtrlC") => { |_| Command::CtrlC }
| preceded!(tag!("vCont"),
alt_complete!(tag!("?") => { |_| Command::VContSupported }
| many0!(do_parse!(
tag!(";") >>
action: alt_complete!(tag!("c") => { |_| VCont::Continue }
| preceded!(tag!("C"), hex_byte) => { |sig| VCont::ContinueWithSignal(sig) }
| tag!("s") => { |_| VCont::Step }
| preceded!(tag!("S"), hex_byte) => { |sig| VCont::StepWithSignal(sig) }
| tag!("t") => { |_| VCont::Stop }
| do_parse!(tag!("r") >>
start: hex_value >>
tag!(",") >>
end: hex_value >>
(start, end)) => { |(start, end)| VCont::RangeStep(start..end) }
) >>
thread: opt!(complete!(preceded!(tag!(":"), parse_thread_id))) >>
(action, thread)
)) => { |actions| Command::VCont(actions) }
)) => {
|c| c
}
| preceded!(tag!("vKill;"), hex_value) => {
|pid| Command::Kill(Some(pid))
}
| preceded!(tag!("v"), take_till!(|_| { false })) => {
|_| Command::UnknownV
})
}
named!(parse_h_packet<&[u8], ThreadId>,
preceded!(tag!("Hg"), parse_thread_id));
named!(parse_d_packet<&[u8], Option<u64>>,
alt_complete!(preceded!(tag!("D;"), hex_value) => {
|pid| Some(pid)
}
| tag!("D") => { |_| None }));
#[derive(Copy, Clone)]
enum ZAction {
Insert,
Remove,
}
named!(parse_z_action<&[u8], ZAction>,
alt_complete!(tag!("z") => { |_| ZAction::Remove } |
tag!("Z") => { |_| ZAction::Insert }));
#[derive(Copy, Clone)]
enum ZType {
SoftwareBreakpoint,
HardwareBreakpoint,
WriteWatchpoint,
ReadWatchpoint,
AccessWatchpoint,
}
named!(parse_z_type<&[u8], ZType>,
alt_complete!(tag!("0") => { |_| ZType::SoftwareBreakpoint } |
tag!("1") => { |_| ZType::HardwareBreakpoint } |
tag!("2") => { |_| ZType::WriteWatchpoint } |
tag!("3") => { |_| ZType::ReadWatchpoint } |
tag!("4") => { |_| ZType::AccessWatchpoint }));
named!(parse_cond_or_command_expression<&[u8], Bytecode>,
do_parse!(tag!("X") >>
len: hex_value >>
tag!(",") >>
expr: take!(len) >>
(Bytecode { bytecode: expr.to_vec() })));
named!(parse_condition_list<&[u8], Vec<Bytecode>>,
do_parse!(tag!(";") >>
list: many1!(parse_cond_or_command_expression) >>
(list)));
fn maybe_condition_list<'a>(i: &'a [u8]) -> IResult<&'a [u8], Option<Vec<Bytecode>>> {
match parse_condition_list(i) {
Done(rest, v) => Done(rest, Some(v)),
Incomplete(_i) => Done(i, None),
Error(_) => Done(i, None),
}
}
named!(parse_command_list<&[u8], Vec<Bytecode>>,
do_parse!(tag!(";cmds") >>
list: alt_complete!(do_parse!(persist_flag: hex_value >>
tag!(",") >>
cmd_list: many1!(parse_cond_or_command_expression) >>
(cmd_list)) |
many1!(parse_cond_or_command_expression)) >>
(list)));
fn maybe_command_list<'a>(i: &'a [u8]) -> IResult<&'a [u8], Option<Vec<Bytecode>>> {
match parse_command_list(i) {
Done(rest, v) => Done(rest, Some(v)),
Incomplete(_i) => Done(i, None),
Error(e) => Error(e),
}
}
named!(parse_cond_and_command_list<&[u8], (Option<Vec<Bytecode>>,
Option<Vec<Bytecode>>)>,
do_parse!(cond_list: maybe_condition_list >>
cmd_list: maybe_command_list >>
(cond_list, cmd_list)));
fn parse_z_packet(i: &[u8]) -> IResult<&[u8], Command> {
let (rest, (action, type_, addr, kind)) = try_parse!(
i,
do_parse!(
action: parse_z_action
>> type_: parse_z_type
>> tag!(",") >> addr: hex_value
>> tag!(",") >> kind: hex_value
>> (action, type_, addr, kind)
)
);
return match action {
ZAction::Insert => insert_command(rest, type_, addr, kind),
ZAction::Remove => Done(rest, remove_command(type_, addr, kind)),
};
fn insert_command(rest: &[u8], type_: ZType, addr: u64, kind: u64) -> IResult<&[u8], Command> {
match type_ {
ZType::SoftwareBreakpoint | ZType::HardwareBreakpoint => {
let (rest, (cond_list, cmd_list)) = parse_cond_and_command_list(rest).unwrap();
let c = (match type_ {
ZType::SoftwareBreakpoint => Command::InsertSoftwareBreakpoint,
ZType::HardwareBreakpoint => Command::InsertHardwareBreakpoint,
_ => panic!("cannot get here"),
})(Breakpoint::new(addr, kind, cond_list, cmd_list));
Done(rest, c)
}
ZType::WriteWatchpoint => Done(
rest,
Command::InsertWriteWatchpoint(Watchpoint::new(addr, kind)),
),
ZType::ReadWatchpoint => Done(
rest,
Command::InsertReadWatchpoint(Watchpoint::new(addr, kind)),
),
ZType::AccessWatchpoint => Done(
rest,
Command::InsertAccessWatchpoint(Watchpoint::new(addr, kind)),
),
}
}
fn remove_command<'a>(type_: ZType, addr: u64, kind: u64) -> Command<'a> {
match type_ {
ZType::SoftwareBreakpoint => {
Command::RemoveSoftwareBreakpoint(Breakpoint::new(addr, kind, None, None))
}
ZType::HardwareBreakpoint => {
Command::RemoveHardwareBreakpoint(Breakpoint::new(addr, kind, None, None))
}
ZType::WriteWatchpoint => Command::RemoveWriteWatchpoint(Watchpoint::new(addr, kind)),
ZType::ReadWatchpoint => Command::RemoveReadWatchpoint(Watchpoint::new(addr, kind)),
ZType::AccessWatchpoint => Command::RemoveAccessWatchpoint(Watchpoint::new(addr, kind)),
}
}
}
fn command(i: &[u8]) -> IResult<&[u8], Command> {
alt!(i,
tag!("!") => { |_| Command::EnableExtendedMode }
| tag!("?") => { |_| Command::TargetHaltReason }
| tag!("c") => { |_| Command::VCont(vec![(VCont::Continue, None)]) }
| parse_d_packet => { |pid| Command::Detach(pid) }
| tag!("g") => { |_| Command::ReadGeneralRegisters }
| write_general_registers => { |bytes| Command::WriteGeneralRegisters(bytes) }
| parse_h_packet => { |thread_id| Command::SetCurrentThread(thread_id) }
| tag!("k") => { |_| Command::Kill(None) }
| read_memory => { |(addr, length)| Command::ReadMemory(MemoryRegion::new(addr, length)) }
| write_memory => { |(addr, length, bytes)| Command::WriteMemory(MemoryRegion::new(addr, length), bytes) }
| read_register => { |regno| Command::ReadRegister(regno) }
| write_register => { |(regno, bytes)| Command::WriteRegister(regno, bytes) }
| query => { |q| Command::Query(q) }
| tag!("r") => { |_| Command::Reset }
| preceded!(tag!("R"), take!(2)) => { |_| Command::Reset }
| parse_ping_thread => { |thread_id| Command::PingThread(thread_id) }
| v_command => { |command| command }
| write_memory_binary => { |(addr, length, bytes)| Command::WriteMemory(MemoryRegion::new(addr, length), bytes) }
| parse_z_packet => { |command| command }
)
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum Error {
Error(u8),
Unimplemented,
}
#[derive(Clone, Debug)]
pub struct FileData(pub String);
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub enum ProcessType {
Attached,
Created,
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
pub enum StopReason {
Signal(u8),
Exited(u64, u8),
ExitedWithSignal(u64, u8),
ThreadExited(ThreadId, u64),
NoMoreThreads,
}
pub trait Handler {
fn query_supported_features(&self) -> Vec<String> {
vec![]
}
fn attached(&self, _pid: Option<u64>) -> Result<ProcessType, Error>;
fn detach(&self, _pid: Option<u64>) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn kill(&self, _pid: Option<u64>) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn ping_thread(&self, _id: ThreadId) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn read_memory(&self, _region: MemoryRegion) -> Result<Vec<u8>, Error> {
Err(Error::Unimplemented)
}
fn write_memory(&self, _address: u64, _bytes: &[u8]) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn read_register(&self, _register: u64) -> Result<Vec<u8>, Error> {
Err(Error::Unimplemented)
}
fn write_register(&self, _register: u64, _contents: &[u8]) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn read_general_registers(&self) -> Result<Vec<u8>, Error> {
Err(Error::Unimplemented)
}
fn write_general_registers(&self, _contents: &[u8]) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn current_thread(&self) -> Result<Option<ThreadId>, Error> {
Ok(None)
}
fn set_current_thread(&self, _id: ThreadId) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn search_memory(
&self,
_address: u64,
_length: u64,
_bytes: &[u8],
) -> Result<Option<u64>, Error> {
Err(Error::Unimplemented)
}
fn halt_reason(&self) -> Result<StopReason, Error>;
fn invoke(&self, _: &[u8]) -> Result<String, Error> {
Err(Error::Unimplemented)
}
fn set_address_randomization(&self, _enable: bool) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn catch_syscalls(&self, _syscalls: Option<Vec<u64>>) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn set_pass_signals(&self, _signals: Vec<u64>) -> Result<(), Error> {
Ok(())
}
fn set_program_signals(&self, _signals: Vec<u64>) -> Result<(), Error> {
Ok(())
}
fn thread_info(&self, _thread: ThreadId) -> Result<String, Error> {
Err(Error::Unimplemented)
}
fn thread_list(&self, _reset: bool) -> Result<Vec<ThreadId>, Error> {
Err(Error::Unimplemented)
}
fn process_list(&self, _reset: bool) -> Result<Vec<ProcessInfo>, Error> {
Err(Error::Unimplemented)
}
fn read_feature(&self, _name: String, _offset: u64, _length: u64) -> Result<FileData, Error> {
Err(Error::Unimplemented)
}
fn insert_software_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn insert_hardware_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn insert_write_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn insert_read_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn insert_access_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn remove_software_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn remove_hardware_breakpoint(&self, _breakpoint: Breakpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn remove_write_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn remove_read_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn remove_access_watchpoint(&self, _watchpoint: Watchpoint) -> Result<(), Error> {
Err(Error::Unimplemented)
}
fn query_supported_vcont(&self) -> Result<Cow<'static, [VContFeature]>, Error> {
Err(Error::Unimplemented)
}
fn vcont(&self, _request: Vec<(VCont, Option<ThreadId>)>) -> Result<StopReason, Error> {
Err(Error::Unimplemented)
}
fn host_info(&self) -> Result<String, Error> {
Err(Error::Unimplemented)
}
fn should_cont(&self) -> Option<VCont>;
}
#[derive(Debug)]
pub enum Response<'a> {
Empty,
Ok,
Error(u8),
String(Cow<'a, str>),
Output(String),
Bytes(Vec<u8>),
CurrentThread(Option<ThreadId>),
ProcessType(ProcessType),
Stopped(StopReason),
SearchResult(Option<u64>),
VContFeatures(Cow<'static, [VContFeature]>),
ThreadList(Vec<ThreadId>),
ProcessList(Vec<ProcessInfo>),
File(FileData),
}
impl<'a, T> From<Result<T, Error>> for Response<'a>
where
Response<'a>: From<T>,
{
fn from(result: Result<T, Error>) -> Self {
match result {
Result::Ok(val) => val.into(),
Result::Err(Error::Error(val)) => Response::Error(val),
Result::Err(Error::Unimplemented) => {
println!("Unimplemented!");
Response::Empty
}
}
}
}
impl<'a> From<()> for Response<'a> {
fn from(_: ()) -> Self {
Response::Ok
}
}
impl<'a> From<Vec<u8>> for Response<'a> {
fn from(response: Vec<u8>) -> Self {
Response::Bytes(response)
}
}
impl<'a> From<FileData> for Response<'a> {
fn from(response: FileData) -> Self {
Response::File(response)
}
}
impl<'a> From<Option<ThreadId>> for Response<'a> {
fn from(response: Option<ThreadId>) -> Self {
Response::CurrentThread(response)
}
}
impl<'a> From<Option<u64>> for Response<'a> {
fn from(response: Option<u64>) -> Self {
Response::SearchResult(response)
}
}
impl<'a> From<ProcessType> for Response<'a> {
fn from(process_type: ProcessType) -> Self {
Response::ProcessType(process_type)
}
}
impl<'a> From<StopReason> for Response<'a> {
fn from(reason: StopReason) -> Self {
Response::Stopped(reason)
}
}
impl<'a> From<String> for Response<'a> {
fn from(reason: String) -> Self {
Response::String(Cow::Owned(reason) as Cow<str>)
}
}
impl<'a> From<Cow<'static, [VContFeature]>> for Response<'a> {
fn from(features: Cow<'static, [VContFeature]>) -> Self {
Response::VContFeatures(features)
}
}
impl<'a> From<Vec<ThreadId>> for Response<'a> {
fn from(threads: Vec<ThreadId>) -> Self {
Response::ThreadList(threads)
}
}
impl<'a> From<Vec<ProcessInfo>> for Response<'a> {
fn from(procs: Vec<ProcessInfo>) -> Self {
Response::ProcessList(procs)
}
}
fn get_thread_id(thread_id: ThreadId) -> String {
let mut tid = String::new();
match thread_id.tid {
Id::All => tid.push_str("-1"),
Id::Any => tid.push_str("0"),
Id::Id(num) => tid.push_str(&format!("{:x}", num)),
};
tid
}
fn get_process_info(p: &ProcessInfo) -> String {
let mut out = String::new();
out.push_str("pid:");
match p.pid {
Id::All => out.push_str("-1"),
Id::Any => out.push_str("0"),
Id::Id(num) => out.push_str(&format!("{:x}", num)),
};
out.push_str(&format!("name:{}", p.name));
out.push_str(&format!("triple:{}", p.triple));
out
}
impl<'a> From<Response<'a>> for Vec<u8> {
fn from(response: Response) -> Vec<u8> {
trace!("Response: {:?}", response);
let mut rsp = String::new();
match response {
Response::Ok => "OK".into(),
Response::Empty => "".into(),
Response::Error(val) => format!("E{:02x}", val),
Response::String(s) => format!("{}", s),
Response::Output(s) => format!("O{}", s.as_bytes().to_hex()),
Response::Bytes(bytes) => bytes.to_hex(),
Response::File(data) => {
if data.0.is_empty() {
"l".into()
} else {
format!("m{}", data.0 )
}
}
Response::CurrentThread(tid) => {
match tid {
None => "OK".into(),
Some(thread_id) => format!("QC{}", get_thread_id(thread_id)),
}
}
Response::ProcessType(process_type) => match process_type {
ProcessType::Attached => "1".into(),
ProcessType::Created => "0".into(),
},
Response::SearchResult(maybe_addr) => match maybe_addr {
Some(addr) => format!("1,{:x}", addr),
None => "0".into(),
},
Response::Stopped(stop_reason) => {
match stop_reason {
StopReason::Signal(signo) => format!("S{:02x}", signo),
StopReason::Exited(pid, status) => {
format!("W{:02x};process:{:x}", status, pid)
}
StopReason::ExitedWithSignal(pid, status) => {
format!("X{:x};process:{:x}", status, pid)
}
StopReason::ThreadExited(thread_id, status) => {
format!("w{:x}{};", status, get_thread_id(thread_id))
}
StopReason::NoMoreThreads => "N".to_string(),
}
}
Response::VContFeatures(features) => {
rsp.push_str("vCont");
for &feature in &*features {
rsp.push_str(&format!(";{}", feature as u8 as char));
}
rsp
}
Response::ThreadList(threads) => {
if threads.is_empty() {
"l".into()
} else {
rsp.push_str("m");
for (i, &id) in threads.iter().enumerate() {
if i != 0 {
rsp.push_str(",");
}
rsp.push_str(&get_thread_id(id));
}
rsp
}
}
Response::ProcessList(procs) => {
if procs.is_empty() {
"E00".into()
} else {
rsp.push_str("m");
for (i, p) in procs.iter().enumerate() {
if i != 0 {
rsp.push_str(",");
}
rsp.push_str(&get_process_info(p));
}
rsp
}
}
}
.as_bytes()
.to_vec()
}
}
fn handle_supported_features<'a, H>(
handler: &H,
_features: &[GDBFeatureSupported<'a>],
) -> Response<'static>
where
H: Handler,
{
let mut features = vec![
format!("PacketSize={}", BUF_SIZE),
];
let mut new_features = handler.query_supported_features();
features.append(&mut new_features);
Response::String(Cow::Owned(features.join(";")) as Cow<str>)
}
pub fn handle_packet<'a, H>(data: &[u8], handler: &'a H) -> Result<Response<'a>, Error>
where
H: Handler,
{
let mut _no_ack_mode = false;
let response = if let Done(_, command) = command(data) {
debug!(
"Successfully parsed command: {} into {:?}",
String::from_utf8_lossy(data),
command
);
match command {
Command::EnableExtendedMode => Response::Ok,
Command::TargetHaltReason => handler.halt_reason()?.into(),
Command::ReadGeneralRegisters => handler.read_general_registers()?.into(),
Command::WriteGeneralRegisters(bytes) => {
handler.write_general_registers(&bytes[..])?.into()
}
Command::Kill(None) => {
drop(handler.kill(None));
Response::Empty
}
Command::Kill(pid) => handler.kill(pid)?.into(),
Command::Reset => Response::Empty,
Command::ReadRegister(regno) => handler.read_register(regno)?.into(),
Command::WriteRegister(regno, bytes) => {
handler.write_register(regno, &bytes[..])?.into()
}
Command::ReadMemory(region) => handler.read_memory(region)?.into(),
Command::WriteMemory(region, bytes) => {
if region.length as usize != bytes.len() {
Response::Error(1)
} else {
handler.write_memory(region.address, &bytes[..])?.into()
}
}
Command::SetCurrentThread(thread_id) => handler.set_current_thread(thread_id)?.into(),
Command::Detach(pid) => handler.detach(pid)?.into(),
Command::Query(Query::Attached(pid)) => handler.attached(pid)?.into(),
Command::Query(Query::CurrentThread) => handler.current_thread()?.into(),
Command::Query(Query::Invoke(cmd)) => match handler.invoke(&cmd[..]) {
Result::Ok(val) => {
if val.is_empty() {
Response::Ok
} else {
Response::Output(val)
}
}
Result::Err(Error::Error(val)) => Response::Error(val),
Result::Err(Error::Unimplemented) => Response::Empty,
},
Command::Query(Query::SearchMemory {
address,
length,
bytes,
}) => handler.search_memory(address, length, &bytes[..])?.into(),
Command::Query(Query::SupportedFeatures(features)) => {
handle_supported_features(handler, &features)
}
Command::Query(Query::StartNoAckMode) => {
_no_ack_mode = true;
Response::Empty
}
Command::Query(Query::AddressRandomization(randomize)) => {
handler.set_address_randomization(randomize)?.into()
}
Command::Query(Query::CatchSyscalls(calls)) => handler.catch_syscalls(calls)?.into(),
Command::Query(Query::PassSignals(signals)) => {
handler.set_pass_signals(signals)?.into()
}
Command::Query(Query::ProgramSignals(signals)) => {
handler.set_program_signals(signals)?.into()
}
Command::Query(Query::ThreadInfo(thread_info)) => {
handler.thread_info(thread_info)?.into()
}
Command::Query(Query::ThreadList(reset)) => handler.thread_list(reset)?.into(),
Command::Query(Query::ProcessList(reset)) => handler.process_list(reset)?.into(),
Command::Query(Query::HostInfo) => handler.host_info()?.into(),
Command::Query(Query::FeatureRead {
name,
offset,
length,
}) => handler.read_feature(name, offset, length)?.into(),
Command::PingThread(thread_id) => handler.ping_thread(thread_id)?.into(),
Command::CtrlC => Response::Empty,
Command::UnknownV => Response::Empty,
Command::InsertSoftwareBreakpoint(bp) => handler.insert_software_breakpoint(bp)?.into(),
Command::InsertHardwareBreakpoint(bp) => handler.insert_hardware_breakpoint(bp)?.into(),
Command::InsertWriteWatchpoint(wp) => handler.insert_write_watchpoint(wp)?.into(),
Command::InsertReadWatchpoint(wp) => handler.insert_read_watchpoint(wp)?.into(),
Command::InsertAccessWatchpoint(wp) => handler.insert_access_watchpoint(wp)?.into(),
Command::RemoveSoftwareBreakpoint(bp) => handler.remove_software_breakpoint(bp)?.into(),
Command::RemoveHardwareBreakpoint(bp) => handler.remove_hardware_breakpoint(bp)?.into(),
Command::RemoveWriteWatchpoint(wp) => handler.remove_write_watchpoint(wp)?.into(),
Command::RemoveReadWatchpoint(wp) => handler.remove_read_watchpoint(wp)?.into(),
Command::RemoveAccessWatchpoint(wp) => handler.remove_access_watchpoint(wp)?.into(),
Command::VContSupported => handler.query_supported_vcont()?.into(),
Command::VCont(list) => handler.vcont(list)?.into(),
}
} else {
info!(
"Command could not be parsed: {}",
String::from_utf8_lossy(data)
);
Response::Empty
};
Ok(response)
}
#[test]
fn test_gdbfeaturesupported() {
assert_eq!(
gdbfeaturesupported(&b"multiprocess+"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::Yes(GDBFeature::multiprocess), FeatureSupported::Yes)
)
);
assert_eq!(
gdbfeaturesupported(&b"xmlRegisters=i386"[..]),
Done(
&b""[..],
GDBFeatureSupported(
Known::Yes(GDBFeature::xmlRegisters),
FeatureSupported::Value("i386")
)
)
);
assert_eq!(
gdbfeaturesupported(&b"qRelocInsn-"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::Yes(GDBFeature::qRelocInsn), FeatureSupported::No)
)
);
assert_eq!(
gdbfeaturesupported(&b"vfork-events+"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::Yes)
)
);
assert_eq!(
gdbfeaturesupported(&b"vfork-events-"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::No)
)
);
assert_eq!(
gdbfeaturesupported(&b"unknown-feature+"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::No("unknown-feature"), FeatureSupported::Yes)
)
);
assert_eq!(
gdbfeaturesupported(&b"unknown-feature-"[..]),
Done(
&b""[..],
GDBFeatureSupported(Known::No("unknown-feature"), FeatureSupported::No)
)
);
}
#[test]
fn test_gdbfeature() {
assert_eq!(
gdbfeature(&b"multiprocess"[..]),
Done(&b""[..], Known::Yes(GDBFeature::multiprocess))
);
assert_eq!(
gdbfeature(&b"fork-events"[..]),
Done(&b""[..], Known::Yes(GDBFeature::fork_events))
);
assert_eq!(
gdbfeature(&b"some-unknown-feature"[..]),
Done(&b""[..], Known::No("some-unknown-feature"))
);
}
#[test]
fn test_query() {
let b = concat!(
"qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;",
"vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;",
"xmlRegisters=i386"
);
assert_eq!(
query(b.as_bytes()),
Done(
&b""[..],
Query::SupportedFeatures(vec![
GDBFeatureSupported(Known::Yes(GDBFeature::multiprocess), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::swbreak), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::hwbreak), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::qRelocInsn), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::fork_events), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::vfork_events), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::exec_events), FeatureSupported::Yes),
GDBFeatureSupported(
Known::Yes(GDBFeature::vContSupported),
FeatureSupported::Yes
),
GDBFeatureSupported(Known::Yes(GDBFeature::QThreadEvents), FeatureSupported::Yes),
GDBFeatureSupported(Known::Yes(GDBFeature::no_resumed), FeatureSupported::Yes),
GDBFeatureSupported(
Known::Yes(GDBFeature::xmlRegisters),
FeatureSupported::Value("i386")
),
])
)
);
}
#[test]
fn test_hex_value() {
assert_eq!(hex_value(&b""[..]), Incomplete(Needed::Size(1)));
assert_eq!(hex_value(&b","[..]), Error(nom::ErrorKind::TakeWhile1));
assert_eq!(hex_value(&b"a"[..]), Done(&b""[..], 0xa));
assert_eq!(hex_value(&b"10,"[..]), Done(&b","[..], 0x10));
assert_eq!(hex_value(&b"ff"[..]), Done(&b""[..], 0xff));
}
#[test]
fn test_parse_thread_id_element() {
assert_eq!(parse_thread_id_element(&b"0"[..]), Done(&b""[..], Id::Any));
assert_eq!(parse_thread_id_element(&b"-1"[..]), Done(&b""[..], Id::All));
assert_eq!(
parse_thread_id_element(&b"23"[..]),
Done(&b""[..], Id::Id(0x23))
);
}
#[test]
fn test_parse_thread_id() {
assert_eq!(
parse_thread_id(&b"0"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::Any,
tid: Id::Any
}
)
);
assert_eq!(
parse_thread_id(&b"-1"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::All,
tid: Id::Any
}
)
);
assert_eq!(
parse_thread_id(&b"23"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::Id(0x23),
tid: Id::Any
}
)
);
assert_eq!(
parse_thread_id(&b"p23"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::Id(0x23),
tid: Id::All
}
)
);
assert_eq!(
parse_thread_id(&b"p0.0"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::Any,
tid: Id::Any
}
)
);
assert_eq!(
parse_thread_id(&b"p-1.23"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::All,
tid: Id::Id(0x23)
}
)
);
assert_eq!(
parse_thread_id(&b"pff.23"[..]),
Done(
&b""[..],
ThreadId {
pid: Id::Id(0xff),
tid: Id::Id(0x23)
}
)
);
}
#[test]
fn test_parse_v_commands() {
assert_eq!(
v_command(&b"vKill;33"[..]),
Done(&b""[..], Command::Kill(Some(0x33)))
);
assert_eq!(v_command(&b"vCtrlC"[..]), Done(&b""[..], Command::CtrlC));
assert_eq!(
v_command(&b"vMustReplyEmpty"[..]),
Done(&b""[..], Command::UnknownV)
);
assert_eq!(
v_command(&b"vFile:close:0"[..]),
Done(&b""[..], Command::UnknownV)
);
assert_eq!(
v_command(&b"vCont?"[..]),
Done(&b""[..], Command::VContSupported)
);
assert_eq!(
v_command(&b"vCont"[..]),
Done(&b""[..], Command::VCont(Vec::new()))
);
assert_eq!(
v_command(&b"vCont;c"[..]),
Done(&b""[..], Command::VCont(vec![(VCont::Continue, None)]))
);
assert_eq!(
v_command(&b"vCont;r1,2:p34.56;SAD:-1;c"[..]),
Done(
&b""[..],
Command::VCont(vec![
(
VCont::RangeStep(1..2),
Some(ThreadId {
pid: Id::Id(0x34),
tid: Id::Id(0x56)
})
),
(
VCont::StepWithSignal(0xAD),
Some(ThreadId {
pid: Id::All,
tid: Id::Any
})
),
(VCont::Continue, None)
])
)
);
}
#[test]
fn test_parse_d_packets() {
assert_eq!(parse_d_packet(&b"D"[..]), Done(&b""[..], None));
assert_eq!(parse_d_packet(&b"D;f0"[..]), Done(&b""[..], Some(240)));
}
#[test]
fn test_parse_write_memory() {
assert_eq!(
write_memory(&b"Mf0,3:ff0102"[..]),
Done(&b""[..], (240, 3, vec!(255, 1, 2)))
);
}
#[test]
fn test_parse_write_memory_binary() {
assert_eq!(
write_memory_binary(&b"Xf0,1: "[..]),
Done(&b""[..], (240, 1, vec!(0x20)))
);
assert_eq!(
write_memory_binary(&b"X90,10:}\x5d"[..]),
Done(&b""[..], (144, 16, vec!(0x7d)))
);
assert_eq!(
write_memory_binary(&b"X5,100:}\x5d}\x03"[..]),
Done(&b""[..], (5, 256, vec!(0x7d, 0x23)))
);
assert_eq!(
write_memory_binary(&b"Xff,2:}\x04\x9a"[..]),
Done(&b""[..], (255, 2, vec!(0x24, 0x9a)))
);
assert_eq!(
write_memory_binary(&b"Xff,2:\xce}\x0a\x9a"[..]),
Done(&b""[..], (255, 2, vec!(0xce, 0x2a, 0x9a)))
);
}
#[test]
fn test_parse_qrcmd() {
assert_eq!(
query(&b"qRcmd,736f6d657468696e67"[..]),
Done(&b""[..], Query::Invoke(b"something".to_vec()))
);
}
#[test]
fn test_parse_randomization() {
assert_eq!(
query(&b"QDisableRandomization:0"[..]),
Done(&b""[..], Query::AddressRandomization(true))
);
assert_eq!(
query(&b"QDisableRandomization:1"[..]),
Done(&b""[..], Query::AddressRandomization(false))
);
}
#[test]
fn test_parse_syscalls() {
assert_eq!(
query(&b"QCatchSyscalls:0"[..]),
Done(&b""[..], Query::CatchSyscalls(None))
);
assert_eq!(
query(&b"QCatchSyscalls:1"[..]),
Done(&b""[..], Query::CatchSyscalls(Some(vec!())))
);
assert_eq!(
query(&b"QCatchSyscalls:1;0;1;ff"[..]),
Done(&b""[..], Query::CatchSyscalls(Some(vec!(0, 1, 255))))
);
}
#[test]
fn test_parse_signals() {
assert_eq!(
query(&b"QPassSignals:"[..]),
Done(&b""[..], Query::PassSignals(vec!()))
);
assert_eq!(
query(&b"QPassSignals:0"[..]),
Done(&b""[..], Query::PassSignals(vec!(0)))
);
assert_eq!(
query(&b"QPassSignals:1;2;ff"[..]),
Done(&b""[..], Query::PassSignals(vec!(1, 2, 255)))
);
assert_eq!(
query(&b"QProgramSignals:0"[..]),
Done(&b""[..], Query::ProgramSignals(vec!(0)))
);
assert_eq!(
query(&b"QProgramSignals:1;2;ff"[..]),
Done(&b""[..], Query::ProgramSignals(vec!(1, 2, 255)))
);
}
#[test]
fn test_thread_info() {
assert_eq!(
query(&b"qThreadExtraInfo,ffff"[..]),
Done(
&b""[..],
Query::ThreadInfo(ThreadId {
pid: Id::Id(65535),
tid: Id::Any
})
)
);
}
#[test]
fn test_thread_list() {
assert_eq!(
query(&b"qfThreadInfo"[..]),
Done(&b""[..], Query::ThreadList(true))
);
assert_eq!(
query(&b"qsThreadInfo"[..]),
Done(&b""[..], Query::ThreadList(false))
);
}
#[test]
fn test_parse_write_register() {
assert_eq!(
write_register(&b"Pff=1020"[..]),
Done(&b""[..], (255, vec!(16, 32)))
);
}
#[test]
fn test_parse_write_general_registers() {
assert_eq!(
write_general_registers(&b"G0001020304"[..]),
Done(&b""[..], vec!(0, 1, 2, 3, 4))
);
}
#[cfg(test)]
macro_rules! bytecode {
($elem:expr; $n:expr) => (Bytecode { bytecode: vec![$elem; $n] });
($($x:expr),*) => (Bytecode { bytecode: vec!($($x),*) })
}
#[test]
fn test_breakpoints() {
assert_eq!(
parse_z_packet(&b"Z0,1ff,0"[..]),
Done(
&b""[..],
Command::InsertSoftwareBreakpoint(Breakpoint::new(0x1ff, 0, None, None))
)
);
assert_eq!(
parse_z_packet(&b"z0,1fff,0"[..]),
Done(
&b""[..],
Command::RemoveSoftwareBreakpoint(Breakpoint::new(0x1fff, 0, None, None))
)
);
assert_eq!(
parse_z_packet(&b"Z1,ae,0"[..]),
Done(
&b""[..],
Command::InsertHardwareBreakpoint(Breakpoint::new(0xae, 0, None, None))
)
);
assert_eq!(
parse_z_packet(&b"z1,aec,0"[..]),
Done(
&b""[..],
Command::RemoveHardwareBreakpoint(Breakpoint::new(0xaec, 0, None, None))
)
);
assert_eq!(
parse_z_packet(&b"Z2,4cc,2"[..]),
Done(
&b""[..],
Command::InsertWriteWatchpoint(Watchpoint::new(0x4cc, 2))
)
);
assert_eq!(
parse_z_packet(&b"z2,4ccf,4"[..]),
Done(
&b""[..],
Command::RemoveWriteWatchpoint(Watchpoint::new(0x4ccf, 4))
)
);
assert_eq!(
parse_z_packet(&b"Z3,7777,4"[..]),
Done(
&b""[..],
Command::InsertReadWatchpoint(Watchpoint::new(0x7777, 4))
)
);
assert_eq!(
parse_z_packet(&b"z3,77778,8"[..]),
Done(
&b""[..],
Command::RemoveReadWatchpoint(Watchpoint::new(0x77778, 8))
)
);
assert_eq!(
parse_z_packet(&b"Z4,7777,10"[..]),
Done(
&b""[..],
Command::InsertAccessWatchpoint(Watchpoint::new(0x7777, 16))
)
);
assert_eq!(
parse_z_packet(&b"z4,77778,20"[..]),
Done(
&b""[..],
Command::RemoveAccessWatchpoint(Watchpoint::new(0x77778, 32))
)
);
assert_eq!(
parse_z_packet(&b"Z0,1ff,2;X1,0"[..]),
Done(
&b""[..],
Command::InsertSoftwareBreakpoint(Breakpoint::new(
0x1ff,
2,
Some(vec!(bytecode!(b'0'))),
None
))
)
);
assert_eq!(
parse_z_packet(&b"Z1,1ff,2;X1,0"[..]),
Done(
&b""[..],
Command::InsertHardwareBreakpoint(Breakpoint::new(
0x1ff,
2,
Some(vec!(bytecode!(b'0'))),
None
))
)
);
assert_eq!(
parse_z_packet(&b"Z0,1ff,2;cmdsX1,z"[..]),
Done(
&b""[..],
Command::InsertSoftwareBreakpoint(Breakpoint::new(
0x1ff,
2,
None,
Some(vec!(bytecode!(b'z')))
))
)
);
assert_eq!(
parse_z_packet(&b"Z1,1ff,2;cmdsX1,z"[..]),
Done(
&b""[..],
Command::InsertHardwareBreakpoint(Breakpoint::new(
0x1ff,
2,
None,
Some(vec!(bytecode!(b'z')))
))
)
);
assert_eq!(
parse_z_packet(&b"Z0,1ff,2;X1,0;cmdsX1,a"[..]),
Done(
&b""[..],
Command::InsertSoftwareBreakpoint(Breakpoint::new(
0x1ff,
2,
Some(vec!(bytecode!(b'0'))),
Some(vec!(bytecode!(b'a')))
))
)
);
assert_eq!(
parse_z_packet(&b"Z1,1ff,2;X1,0;cmdsX1,a"[..]),
Done(
&b""[..],
Command::InsertHardwareBreakpoint(Breakpoint::new(
0x1ff,
2,
Some(vec!(bytecode!(b'0'))),
Some(vec!(bytecode!(b'a')))
))
)
);
}
#[test]
fn test_cond_or_command_list() {
assert_eq!(
parse_condition_list(&b";X1,a"[..]),
Done(&b""[..], vec!(bytecode!(b'a')))
);
assert_eq!(
parse_condition_list(&b";X2,ab"[..]),
Done(&b""[..], vec!(bytecode!(b'a', b'b')))
);
assert_eq!(
parse_condition_list(&b";X1,zX1,y"[..]),
Done(&b""[..], vec!(bytecode!(b'z'), bytecode!(b'y')))
);
assert_eq!(
parse_condition_list(&b";X1,zX10,yyyyyyyyyyyyyyyy"[..]),
Done(&b""[..], vec!(bytecode!(b'z'), bytecode![b'y'; 16]))
);
assert_eq!(
parse_command_list(&b";cmdsX1,a"[..]),
Done(&b""[..], vec!(bytecode!(b'a')))
);
assert_eq!(
parse_command_list(&b";cmdsX2,ab"[..]),
Done(&b""[..], vec!(bytecode!(b'a', b'b')))
);
assert_eq!(
parse_command_list(&b";cmdsX1,zX1,y"[..]),
Done(&b""[..], vec!(bytecode!(b'z'), bytecode!(b'y')))
);
assert_eq!(
parse_command_list(&b";cmdsX1,zX10,yyyyyyyyyyyyyyyy"[..]),
Done(&b""[..], vec!(bytecode!(b'z'), bytecode![b'y'; 16]))
);
}