use crate::config::AddressMode;
use crate::packet::{payload, sync};
use crate::types::{branch, trap, Context};
use super::error::Error;
#[derive(Default, Clone, Debug)]
pub struct State {
branches: branch::Map,
last_address: Option<u64>,
address_mode: AddressMode,
}
impl State {
pub fn new(address_mode: AddressMode) -> Self {
Self {
address_mode,
..Default::default()
}
}
pub fn add_branch(&mut self, branch_taken: bool) -> Result<(), Error> {
self.branches
.push_branch_taken(branch_taken)
.map_err(Error::CannotAddBranches)
}
pub fn branches(&self) -> u8 {
self.branches.count()
}
pub fn set_address_mode(&mut self, mode: AddressMode) {
self.address_mode = mode;
}
pub fn reset(&mut self) {
self.branches = Default::default();
self.last_address = None;
}
pub fn payload_builder(
&mut self,
address: u64,
context: Context,
timestamp: Option<u64>,
) -> PayloadBuilder<'_> {
PayloadBuilder {
state: self,
address,
context,
timestamp,
is_branch: false,
}
}
}
#[derive(Debug)]
pub struct PayloadBuilder<'s> {
state: &'s mut State,
address: u64,
context: Context,
timestamp: Option<u64>,
is_branch: bool,
}
impl PayloadBuilder<'_> {
pub fn add_branch(&mut self, branch_taken: bool) -> Result<(), Error> {
self.state.add_branch(branch_taken)?;
self.is_branch = true;
Ok(())
}
pub fn branches(&self) -> u8 {
self.state.branches()
}
}
impl PayloadBuilder<'_> {
pub fn report_sync(mut self) -> sync::Start {
let branch = self.sync_branch_flag();
self.state.last_address = Some(self.address);
sync::Start {
branch,
ctx: self.context(),
address: self.address,
}
}
pub fn report_trap(mut self, thaddr: bool, info: trap::Info) -> sync::Trap {
let branch = self.sync_branch_flag();
self.state.last_address = Some(self.address);
sync::Trap {
branch,
ctx: self.context(),
thaddr,
address: self.address,
info,
}
}
pub fn context(&self) -> sync::Context {
sync::Context {
privilege: self.context.privilege,
time: self.timestamp,
context: self.context.context,
}
}
pub fn report_address<I, D>(
self,
reason: Reason,
) -> Result<payload::InstructionTrace<I, D>, Error> {
let offset = match self.state.address_mode {
AddressMode::Full => 0,
AddressMode::Delta => self.state.last_address.ok_or(Error::NoAddressReported)?,
};
self.state.last_address = Some(self.address);
let address = 0i64
.wrapping_add_unsigned(self.address)
.wrapping_sub_unsigned(offset);
let address = payload::AddressInfo {
address,
notify: reason == Reason::Notify,
updiscon: reason == Reason::Updiscon,
irdepth: None,
};
if self.state.branches.count() != 0 {
Ok(payload::Branch {
branch_map: self.state.branches.take(31),
address: Some(address),
}
.into())
} else {
Ok(address.into())
}
}
pub fn report_full_branchmap(&mut self) -> Option<payload::Branch> {
(self.branches() >= 31).then(|| payload::Branch {
branch_map: self.state.branches.take(31),
address: None,
})
}
fn sync_branch_flag(&mut self) -> bool {
if self.is_branch {
let taken = self
.state
.branches
.pop_taken()
.expect("Branch map is empty when at least one branch is expected");
self.is_branch = false;
!taken
} else {
true
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Reason {
Notify,
Updiscon,
Other,
}