pub mod lattice;
pub mod residue;
pub mod state;
pub use crate::analysis::cpa::residue::{CFG, TerminatingReducer, VEC};
use tracing::{Level, span};
use crate::analysis::cpa::residue::{Residue, ResidueWrapper};
use crate::analysis::cpa::state::{AbstractState, LocationState};
use crate::analysis::pcode_store::PcodeStore;
use std::borrow::Borrow;
use std::collections::VecDeque;
use std::fmt::Debug;
pub trait ConfigurableProgramAnalysis: Sized {
type State: AbstractState + Debug;
type Reducer<'op>: residue::Residue<'op, Self::State>;
}
pub trait RunnableConfigurableProgramAnalysis: ConfigurableProgramAnalysis
where
Self::State: LocationState,
{
fn run_cpa<'op, I: Borrow<Self::State>, P: PcodeStore<'op> + ?Sized>(
&self,
initial: I,
pcode_store: &'op P,
) -> <<Self as ConfigurableProgramAnalysis>::Reducer<'op> as residue::Residue<'op, Self::State>>::Output
where
Self::State: 'op,
{
let initial = initial.borrow();
let mut reducer = <Self::Reducer<'op> as residue::Residue<'op, Self::State>>::new();
let mut waitlist: VecDeque<Self::State> = VecDeque::new();
let mut reached: VecDeque<Self::State> = VecDeque::new();
waitlist.push_front(initial.clone());
reached.push_front(initial.clone());
tracing::debug!("CPA started with initial state: {:?}", initial);
tracing::debug!("Initial waitlist size: 1, reached size: 1");
let mut iteration = 0;
while let Some(state) = waitlist.pop_front() {
let span = span!(Level::DEBUG, "cpa", iteration);
let _enter = span.enter();
iteration += 1;
tracing::trace!("Processing state {:?}", state);
tracing::trace!(
" Waitlist size: {}, Reached size: {}",
waitlist.len(),
reached.len()
);
let op = state.get_operation(pcode_store);
tracing::trace!(
" Operation at state: {:?}",
op.as_ref().map(|p| format!("{:x}", p.as_ref()))
);
let mut new_states = 0;
let mut merged_states = 0;
let mut stopped_states = 0;
for dest_state in op
.iter()
.flat_map(|op| state.transfer(op.as_ref()).into_iter())
{
tracing::trace!(" Transfer produced dest_state: {}", dest_state);
let mut was_merged = false;
for reached_state in reached.iter_mut() {
let old_reached = reached_state.clone();
if reached_state.merge(&dest_state).merged() {
tracing::debug!(" Merged dest_state into existing reached_state");
tracing::debug!(" Merged state: {}", reached_state);
reducer.merged_state(&state, &old_reached, reached_state, &op);
waitlist.push_back(reached_state.clone());
merged_states += 1;
was_merged = true;
}
}
if was_merged {
continue;
}
tracing::debug!("Adding new state without merging: {}", dest_state);
reducer.new_state(&state, &dest_state, &op);
if !dest_state.stop(reached.iter()) {
tracing::trace!(" Adding new state to waitlist and reached");
waitlist.push_back(dest_state.clone());
reached.push_back(dest_state.clone());
new_states += 1;
} else {
tracing::trace!(" State stopped (already covered)");
stopped_states += 1;
}
}
if new_states > 0 || merged_states > 0 || stopped_states > 0 {
tracing::debug!(
"Iteration {} summary: {} new state(s), {} merge(s), {} stopped",
iteration,
new_states,
merged_states,
stopped_states
);
}
}
tracing::debug!(
"CPA completed after {} iterations. Total states reached: {}",
iteration,
reached.len()
);
reducer.finalize()
}
fn with_residue<F>(self, f: F) -> ResidueWrapper<Self, F>
where
for<'op> F: crate::analysis::cpa::residue::ReducerFactoryForState<Self>,
{
ResidueWrapper::wrap(self, f)
}
}
pub trait IntoState<C: ConfigurableProgramAnalysis>: Sized {
fn into_state(self, c: &C) -> C::State;
}
impl<T> RunnableConfigurableProgramAnalysis for T
where
T: ConfigurableProgramAnalysis,
T::State: LocationState,
{
}