blob: 6ed644e3a34280617619af948d49a3accd947499 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2021 CentraleSupelec, CEA-LIST
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Erwan Mahé (CentraleSupelec) - initial API and implementation
*******************************************************************************/
use std::collections::HashMap;
use std::cmp::Reverse;
use crate::core::context::general::GeneralContext;
use crate::core::context::execution::ExecutionContext;
use crate::core::syntax::interaction::*;
use crate::core::syntax::action::*;
use crate::core::syntax::position::*;
use crate::core::trace::{AnalysableMultiTrace,MultiTraceCanal,TraceAction};
use crate::process::log::ProcessLogger;
use crate::core::semantics::frontier::make_frontier;
use crate::core::semantics::shape_execute::shape_execute;
use crate::process::verdicts::*;
use crate::process::hibou_process::*;
use crate::core::trace::*;
use crate::process::queue::ProcessQueue;
use crate::process::symbex::model_symbex::*;
use crate::process::symbex::trace_symbex::*;
use crate::process::deploy_receptions::deploy_original_action_followup;
use crate::diversity::symbex_client::SymbexClient;
use crate::diversity::*;
pub struct ProcessPriorities {
pub emission : i32,
pub reception : i32,
pub in_loop : i32
}
impl ProcessPriorities {
pub fn new(emission : i32,
reception : i32,
in_loop : i32) -> ProcessPriorities {
return ProcessPriorities{emission,reception,in_loop};
}
}
impl std::string::ToString for ProcessPriorities {
fn to_string(&self) -> String {
let mut my_str = format!("emission={:},",self.emission);
my_str.push_str( &format!("reception={:},",self.reception));
my_str.push_str( &format!("in_loop={:}",self.in_loop));
return my_str;
}
}
pub struct HibouProcessManager {
gen_ctx : GeneralContext,
strategy : HibouSearchStrategy,
temporality : HibouProcessTemporality,
pre_filters : Vec<HibouPreFilter>,
// ***
memorized_states : HashMap<u32,MemorizedState>,
process_queue : ProcessQueue,
// ***
frontier_priorities : ProcessPriorities,
// ***
loggers : Vec<Box<dyn ProcessLogger>>
}
impl HibouProcessManager {
pub fn new(gen_ctx : GeneralContext,
strategy : HibouSearchStrategy,
temporality : HibouProcessTemporality,
pre_filters : Vec<HibouPreFilter>,
memorized_states : HashMap<u32,MemorizedState>,
process_queue : ProcessQueue,
frontier_priorities : ProcessPriorities,
loggers : Vec<Box<dyn ProcessLogger>>
) -> HibouProcessManager {
return HibouProcessManager{gen_ctx,strategy,temporality,pre_filters,memorized_states,process_queue,frontier_priorities,loggers};
}
pub fn get_options_as_strings(&self,goal_and_verdict:Option<(&GlobalVerdict,&GlobalVerdict)>) -> Vec<String> {
let mut options_str : Vec<String> = Vec::new();
match goal_and_verdict {
None => {
options_str.push("process=exploration".to_string());
},
Some( (goal,verd) ) => {
options_str.push("process=analysis".to_string());
options_str.push( format!("goal={}", goal.to_string()) );
options_str.push( format!("verdict={}", verd.to_string()) );
}
}
options_str.push( format!("temporality={}", &self.temporality.to_string()) );
options_str.push( format!("strategy={}", &self.strategy.to_string()) );
options_str.push( format!("frontier_priorities=[{}]", &self.frontier_priorities.to_string()) );
{
let mut rem_filter = self.pre_filters.len();
let mut filters_str = "filters=[".to_string();
for filter in &self.pre_filters {
filters_str.push_str( &filter.to_string() );
rem_filter = rem_filter - 1;
if rem_filter > 0 {
filters_str.push_str( "," );
}
}
filters_str.push_str( "]" );
options_str.push( filters_str );
}
return options_str;
}
pub fn init_loggers(&mut self,
exe_ctx : &ExecutionContext,
interaction : &Interaction,
remaining_multi_trace : &Option<AnalysableMultiTrace>) {
for logger in self.loggers.iter_mut() {
(*logger).log_init(interaction, &self.gen_ctx, exe_ctx, remaining_multi_trace);
}
}
pub fn term_loggers(&mut self,
goal_and_verdict:Option<(&GlobalVerdict,&GlobalVerdict)>) {
let options_as_strs = (&self).get_options_as_strings(goal_and_verdict);
for logger in self.loggers.iter_mut() {
(*logger).log_term(&options_as_strs);
}
}
pub fn verdict_loggers(&mut self,
verdict : &CoverageVerdict,
parent_state_id : u32) {
for logger in self.loggers.iter_mut() {
logger.log_verdict(parent_state_id,
verdict);
}
}
pub fn filtered_loggers(&mut self,
action_position : &Position,
executed_action : &ObservableAction,
parent_state_id : u32,
new_state_id : u32,
elim_kind : &FilterEliminationKind) {
let parent_state = self.memorized_states.get(&parent_state_id).unwrap();
for logger in self.loggers.iter_mut() {
logger.log_filtered(&self.gen_ctx,
&parent_state.exe_ctx,
parent_state_id,
new_state_id,
action_position,
executed_action,
elim_kind);
}
}
pub fn unsat_loggers(&mut self,
action_position : &Position,
model_action : &ObservableAction,
trace_action : Option<&TraceAction>,
parent_state_id : u32,
new_state_id : u32) {
let parent_state = self.memorized_states.get(&parent_state_id).unwrap();
for logger in self.loggers.iter_mut() {
logger.log_unsat(&self.gen_ctx,
&parent_state.exe_ctx,
parent_state_id,
new_state_id,
action_position,
trace_action,
model_action);
}
}
pub fn execution_loggers(&mut self,
action_position : &Position,
model_action : &ObservableAction,
trace_action : Option<&TraceAction>,
new_interaction : &Interaction,
new_exe_ctx : &ExecutionContext,
parent_state_id : u32,
new_state_id :u32,
remaining_multi_trace : &Option<AnalysableMultiTrace>) {
for logger in self.loggers.iter_mut() {
logger.log_execution(&self.gen_ctx,
parent_state_id,
new_state_id,
action_position,
trace_action,
model_action,
new_interaction,
new_exe_ctx,
remaining_multi_trace);
}
}
pub fn get_memorized_state(&self, id:u32) -> Option<&MemorizedState> {
return self.memorized_states.get(&id);
}
pub fn forget_state(&mut self, id:u32) {
self.memorized_states.remove(&id);
}
pub fn remember_state(&mut self, id:u32, state:MemorizedState) {
self.memorized_states.insert( id, state );
}
pub fn extract_from_queue(&mut self) -> Option<NextToProcess> {
return self.process_queue.get_next();
}
pub fn enqueue_executions(&mut self, state_id : u32, to_enqueue : Vec<(u32,NextToProcessKind)>) {
let mut to_enqueue_reorganize : HashMap<i32,Vec<(u32,NextToProcessKind)>> = HashMap::new();
for (child_id,child_kind) in to_enqueue {
match &child_kind {
&NextToProcessKind::Execute( ref front_pos ) => {
let mut priority : i32 = 0;
// ***
let parent_state = self.get_memorized_state(state_id).unwrap();
let front_act = (parent_state.interaction).get_sub_interaction(&front_pos).as_leaf();
match front_act.act_kind {
ObservableActionKind::Reception => {
priority = priority + self.frontier_priorities.reception;
},
ObservableActionKind::Emission(_) => {
priority = priority + self.frontier_priorities.emission;
}
}
let loop_depth = (parent_state.interaction).get_loop_depth_at_pos(&front_pos);
if loop_depth > 0 {
priority = priority + self.frontier_priorities.in_loop;
}
// ***
match to_enqueue_reorganize.get_mut(&priority) {
None => {
to_enqueue_reorganize.insert(priority,vec![ (child_id,child_kind) ]);
},
Some( queue ) => {
queue.push((child_id,child_kind) );
}
}
// ***
}
}
}
// ***
let mut to_enqueue_reorganized : Vec<(u32,NextToProcessKind)> = Vec::new();
{
let mut keys : Vec<i32> = to_enqueue_reorganize.keys().cloned().collect();
keys.sort_by_key(|k| Reverse(*k));
for k in keys {
match to_enqueue_reorganize.get_mut(&k) {
None => {},
Some( queue ) => {
to_enqueue_reorganized.append( queue );
}
}
}
}
// ***
match &self.strategy {
&HibouSearchStrategy::DFS => {
to_enqueue_reorganized.reverse();
for (child_id,child_kind) in to_enqueue_reorganized {
self.enqueue_child_node(state_id,child_id,child_kind);
}
},
&HibouSearchStrategy::BFS => {
for (child_id,child_kind) in to_enqueue_reorganized {
self.enqueue_child_node(state_id,child_id,child_kind);
}
}
}
}
fn enqueue_child_node(&mut self,state_id: u32,child_id:u32,child_kind:NextToProcessKind) {
let child = NextToProcess::new(state_id,child_id,child_kind);
match &(self.strategy) {
&HibouSearchStrategy::DFS => {
self.process_queue.insert_item_left(child);
},
&HibouSearchStrategy::BFS => {
self.process_queue.insert_item_right(child);
}
}
}
pub async fn process_next(&mut self,
client : &mut SymbexClient<tonic::transport::Channel>,
parent_state : &MemorizedState,
to_process : &NextToProcess,
new_state_id : u32,
node_counter : u32) -> Option<(Interaction,ExecutionContext,u32,Option<AnalysableMultiTrace>,u32,u32)> {
match &(to_process.kind) {
&NextToProcessKind::Execute( ref position ) => {
let new_depth = parent_state.depth + 1;
let new_loop_depth = parent_state.loop_depth + (parent_state.interaction).get_loop_depth_at_pos(position);
// ***
match self.apply_pre_filters(new_depth,new_loop_depth,node_counter) {
None => {
let mut new_exe_ctx = parent_state.exe_ctx.clone();
match shape_execute(&self.gen_ctx,&mut new_exe_ctx,&parent_state.interaction,position) {
Err(e) => {
panic!("{:?}",e);
},
Ok( (shaped_interaction,shaped_position,shaped_action,needs_scoping) ) => {
match model_symbolic_execution(client,
&self.gen_ctx,
&mut new_exe_ctx,
&shaped_action,
parent_state.diversity_ec_id,
needs_scoping,
&self.temporality).await {
ModelSymbexResult::UnSat => {
self.unsat_loggers(&position,
&shaped_action,
None,
to_process.state_id,
new_state_id);
return None;
},
ModelSymbexResult::Sat( new_diversity_ec_id,
model_firing_conditions,
effective_parameters,
opt_delay ) => {
// ***
match (parent_state.multi_trace).as_ref(){
None => {
let new_interaction = deploy_original_action_followup(&new_exe_ctx,
&shaped_interaction,
&shaped_position,
&shaped_action,
&effective_parameters);
// ***
let trace_act_kind : TraceActionKind;
match &shaped_action.act_kind {
ObservableActionKind::Reception => {
trace_act_kind = TraceActionKind::Reception;
},
ObservableActionKind::Emission(_) => {
trace_act_kind = TraceActionKind::Emission;
}
}
let trace_action = TraceAction{ delay:opt_delay,
lf_id:shaped_action.lf_act.lf_id,
ms_id:shaped_action.ms_id,
act_kind:trace_act_kind,
arguments:effective_parameters};
// ***
self.execution_loggers(&position,
&shaped_action,
Some(&trace_action),
&new_interaction,
&new_exe_ctx,
to_process.state_id,
new_state_id,
&None);
// ***
return Some( (new_interaction,new_exe_ctx,new_diversity_ec_id,None,new_depth,new_loop_depth) );
},
Some( ref multi_trace ) => {
let new_multi_trace : Option<AnalysableMultiTrace>;
let mut head_trace_action_opt : Option<TraceAction> = None;
{
let mut new_canals : Vec<MultiTraceCanal> = Vec::new();
for canal in &multi_trace.canals {
if canal.lifelines.contains(&shaped_action.occupation_before()) {
let mut new_trace = canal.trace.clone();
head_trace_action_opt = Some(new_trace.remove(0));
new_canals.push( MultiTraceCanal{lifelines:canal.lifelines.clone(),trace:new_trace} )
} else {
new_canals.push(canal.clone());
}
}
new_multi_trace = Some( AnalysableMultiTrace::new(new_canals) );
}
let head_trace_action = head_trace_action_opt.unwrap();
// ***
match trace_symbolic_execution(client,
&self.gen_ctx,
&mut new_exe_ctx,
shaped_action.lf_act.lf_id,
shaped_action.ms_id,
&head_trace_action.arguments,
&head_trace_action.delay,
&self.temporality,
new_diversity_ec_id).await {
TraceSymbexResult::UnSat(trace_firing_conditions) => {
self.unsat_loggers(&position,
&shaped_action,
Some(&head_trace_action),
to_process.state_id,
new_state_id);
return None;
},
TraceSymbexResult::Sat(post_trace_analysis_diversity_ec_id,trace_firing_condition) => {
let post_trace_analysis_interaction = deploy_original_action_followup(&new_exe_ctx,
&shaped_interaction,
&shaped_position,
&shaped_action,
&head_trace_action.arguments);
// ***
self.execution_loggers(&position,
&shaped_action,
Some(&head_trace_action),
&post_trace_analysis_interaction,
&new_exe_ctx,
to_process.state_id,
new_state_id,
&new_multi_trace);
// ***
return Some( (post_trace_analysis_interaction,
new_exe_ctx,
post_trace_analysis_diversity_ec_id,
new_multi_trace,
new_depth,
new_loop_depth) );
}
}
// ***
}
}
}
}
}
}
},
Some( elim_kind ) => {
let executed_action = (parent_state.interaction).get_sub_interaction(position).as_leaf();
self.filtered_loggers(&position,
executed_action,
to_process.state_id,
new_state_id,
&elim_kind);
return None;
}
}
},
_ => {
return None;
}
}
}
fn apply_pre_filters(&self, depth : u32, loop_depth : u32, node_counter : u32) -> Option<FilterEliminationKind> {
for pre_filter in &self.pre_filters {
match pre_filter {
HibouPreFilter::MaxProcessDepth( max_depth ) => {
if depth > *max_depth {
return Some( FilterEliminationKind::MaxProcessDepth );
}
},
HibouPreFilter::MaxLoopInstanciation( loop_num ) => {
if loop_depth > *loop_num {
return Some( FilterEliminationKind::MaxLoopInstanciation );
}
},
HibouPreFilter::MaxNodeNumber( max_node_number ) => {
if node_counter >= *max_node_number {
return Some( FilterEliminationKind::MaxNodeNumber );
}
}
}
}
return None;
}
pub fn get_coverage_verdict(&self,interaction:&Interaction,multi_trace:&AnalysableMultiTrace) -> CoverageVerdict {
if multi_trace.length() == 0 {
if interaction.express_empty() {
return CoverageVerdict::Cov;
} else {
return CoverageVerdict::TooShort;
}
} else {
if multi_trace.is_any_component_empty() {
return CoverageVerdict::LackObs;
} else {
return CoverageVerdict::Out;
}
}
}
}