From 8f652f754ccf0483dce217dd02e4fa28bb55c10f Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Fri, 17 May 2024 15:57:44 +0200 Subject: [PATCH] WIP: complet rework of STG --- fuzzers/FRET/src/systemstate/mod.rs | 508 ++++++++++++---------- fuzzers/FRET/src/systemstate/observers.rs | 412 +++++++++++++++--- fuzzers/FRET/src/systemstate/stg.rs | 208 ++++++--- 3 files changed, 780 insertions(+), 348 deletions(-) diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 07a0e66ea1..5915ba8817 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -32,13 +32,13 @@ const NUM_PRIOS: usize = 5; //============================= Struct definitions -#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] pub enum CaptureEvent { - APIStart, - APIEnd, - ISRStart, - ISREnd, - End, + APIStart, /// src,dst + APIEnd, /// src,dst + ISRStart, /// _,dst + ISREnd, /// src,_ + End, /// src,_ #[default] Undefined, } @@ -67,16 +67,18 @@ pub struct RefinedTCB { pub priority: u32, pub base_priority: u32, mutexes_held: u32, - notify_value: u32, + // notify_value: u32, notify_state: u8, } impl PartialEq for RefinedTCB { fn eq(&self, other: &Self) -> bool { - self.task_name == other.task_name && + let ret = self.task_name == other.task_name && self.priority == other.priority && - self.base_priority == other.base_priority - // && self.notify_state == other.notify_state + self.base_priority == other.base_priority; + #[cfg(feature = "do_hash_notify_state")] + let ret = ret && self.notify_state == other.notify_state; + ret } } @@ -85,7 +87,7 @@ impl Hash for RefinedTCB { self.task_name.hash(state); self.priority.hash(state); self.mutexes_held.hash(state); - #[cfg(not(feature = "no_hash_state"))] + #[cfg(feature = "do_hash_notify_state")] self.notify_state.hash(state); // self.notify_value.hash(state); } @@ -101,7 +103,7 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], + // notify_value: input.ulNotifiedValue[0], notify_state: input.ucNotifyState[0], } } @@ -115,27 +117,25 @@ impl RefinedTCB { priority: input.uxPriority, base_priority: input.uxBasePriority, mutexes_held: input.uxMutexesHeld, - notify_value: input.ulNotifiedValue[0], + // notify_value: input.ulNotifiedValue[0], notify_state: input.ucNotifyState[0], } } } } -/// Refined information about the states an execution transitioned between +/// Reduced information about a systems state, without any execution context #[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct RefinedFreeRTOSSystemState { - pub start_tick: u64, - pub end_tick: u64, - edge: (Option,Option), - input_counter: u32, - pub current_task: (RefinedTCB, u32), - ready_list_after: Vec<(RefinedTCB, u32)>, - delay_list_after: Vec<(RefinedTCB, u32)>, - // pub capture_point: String - pub capture_point: (CaptureEvent,String) +pub struct ReducedFreeRTOSSystemState { + // pub tick: u64, + pub current_task: RefinedTCB, + ready_list_after: Vec, + delay_list_after: Vec, + // edge: (Option,Option), + // pub capture_point: (CaptureEvent,String), + // input_counter: u32 } -impl PartialEq for RefinedFreeRTOSSystemState { +impl PartialEq for ReducedFreeRTOSSystemState { fn eq(&self, other: &Self) -> bool { self.current_task == other.current_task && self.ready_list_after == other.ready_list_after && self.delay_list_after == other.delay_list_after @@ -144,42 +144,93 @@ impl PartialEq for RefinedFreeRTOSSystemState { } } -impl Hash for RefinedFreeRTOSSystemState { +impl Hash for ReducedFreeRTOSSystemState { fn hash(&self, state: &mut H) { self.current_task.hash(state); self.ready_list_after.hash(state); self.delay_list_after.hash(state); - // self.last_pc.hash(state); } } -impl RefinedFreeRTOSSystemState { - fn get_time(&self) -> u64 { - self.end_tick-self.start_tick - } +impl ReducedFreeRTOSSystemState { + // fn get_tick(&self) -> u64 { + // self.tick + // } pub fn print_lists(&self) -> String { let mut ret = String::from("+"); for j in self.ready_list_after.iter() { - ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + ret.push_str(format!(" {}", j.task_name).as_str()); } ret.push_str("\n-"); for j in self.delay_list_after.iter() { - ret.push_str(format!(" {}#{}", j.0.task_name, j.1).as_str()); + ret.push_str(format!(" {}", j.task_name).as_str()); } ret } + pub fn get_hash(&self) -> u64 { + let mut h = DefaultHasher::new(); + self.hash(&mut h); + h.finish() + } +} + +// #[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +// pub enum ExecLevel { +// APP = 0, +// API = 1, +// ISR = 2, +// } + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +struct ExecInterval { + start_tick: u64, + end_tick: u64, + start_state: u64, + end_state: u64, + start_capture: (CaptureEvent, String), + end_capture: (CaptureEvent, String), + level: u8, + tick_spend_preempted: u64, + abb: Option +} + +impl ExecInterval { + pub fn get_exec_time(&self) -> u64 { + self.end_tick-self.start_tick-self.tick_spend_preempted + } + pub fn is_valid(&self) -> bool { + self.start_tick != 0 || self.end_tick != 0 + } + pub fn invaildate(&mut self) { + self.start_tick = 0; + self.end_tick = 0; + } + + /// Attach this interval to the later one, keep a record of the time spend preempted + pub fn try_unite_with_later_interval(&mut self, later_interval : &mut Self) -> bool { + if self.end_state!=later_interval.start_state || self.abb!=later_interval.abb || !self.is_valid() || !later_interval.is_valid() { + return false; + } + // assert_eq!(self.end_state, later_interval.start_state); + // assert_eq!(self.abb, later_interval.abb); + later_interval.tick_spend_preempted += self.tick_spend_preempted + (later_interval.start_tick-self.end_tick); + later_interval.start_tick = self.start_tick; + later_interval.start_state = self.start_state; + self.invaildate(); + return true; + } } // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct FreeRTOSSystemStateMetadata { - pub inner: Vec, + pub inner: Vec, trace_length: usize, indices: Vec, // Hashed enumeration of States tcref: isize, } impl FreeRTOSSystemStateMetadata { - pub fn new(inner: Vec) -> Self{ + pub fn new(inner: Vec) -> Self{ let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0} } @@ -218,6 +269,7 @@ libafl_bolts::impl_serdeany!(FreeRTOSSystemStateMetadata); pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, + level: u8, } impl Hash for AtomicBasicBlock { @@ -226,6 +278,7 @@ impl Hash for AtomicBasicBlock { self.start.hash(state); let mut keys : Vec<_> = self.ends.iter().collect(); keys.sort(); + self.level.hash(state); keys.hash(state); } } @@ -236,7 +289,7 @@ impl fmt::Display for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("0x{:#x}, ", end)); } - write!(f, "ABB {{ start: 0x{:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ level: {}, start: 0x{:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) } } impl fmt::Debug for AtomicBasicBlock { @@ -245,7 +298,7 @@ impl fmt::Debug for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("{:#x}, ", end)); } - write!(f, "ABB {{ start: {:#x}, ends: [{}]}}", self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {{ level: {}, start: {:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) } } @@ -258,6 +311,9 @@ impl PartialOrd for AtomicBasicBlock { impl Ord for AtomicBasicBlock { fn cmp(&self, other: &AtomicBasicBlock) -> std::cmp::Ordering { if self.start.cmp(&other.start) == std::cmp::Ordering::Equal { + if self.level.cmp(&other.level) != std::cmp::Ordering::Equal { + return self.level.cmp(&other.level); + } // If the start addresses are equal, compare by 'ends' let end1 = if self.ends.len() == 1 { *self.ends.iter().next().unwrap() as u64 } else { let mut temp = self.ends.iter().collect::>().into_iter().collect::>(); @@ -282,202 +338,202 @@ impl Ord for AtomicBasicBlock { } -fn get_task_names(trace: &Vec) -> HashSet { +fn get_task_names(trace: &Vec) -> HashSet { let mut ret: HashSet<_, _> = HashSet::new(); for state in trace { - ret.insert(state.current_task.0.task_name.to_string()); + ret.insert(state.current_task.task_name.to_string()); } ret } -fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { - let mut abbs_of_task : HashMap>> = HashMap::new(); - let mut last_abb_of_task : HashMap = HashMap::new(); - // iterate over all states and extract atomic basic blocks - // an atomic base block has a single entry and multiple exits - // the cuts between blocks are api calls - // when capture_point is APIEnd, the destination of the edge is the start of an atomic block - // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block - for i in 0..trace.len() { - let curr_name = trace[i].current_task.0.task_name.clone(); - let last : Option<&usize> = last_abb_of_task.get(&curr_name); - match trace[i].capture_point.0 { - CaptureEvent::APIStart => { - // end the last atomic block - if let Some(&l) = last { - let start = trace[l].edge.1.unwrap(); - let end = trace[i].edge.0.unwrap(); - match abbs_of_task.get_mut(&curr_name) { - Some(v) => { - match v.iter_mut().find(|x| x.start==start) { - Some(abb) => { - Rc::get_mut(abb).unwrap().ends.insert(end); - } - None => { - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - } - }; - }, - None => { - let mut v = Vec::new(); - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - abbs_of_task.insert(curr_name, v); - } - } - } else { - // first API call of this task - let mut v = Vec::new(); - let mut t = HashSet::new(); - let end = trace[i].edge.0.unwrap(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); - abbs_of_task.insert(curr_name, v); - } - }, - CaptureEvent::APIEnd => { - match last { - Some(&l) => { - //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); - }, - None => (), - } - last_abb_of_task.insert(curr_name, i); - }, - CaptureEvent::ISRStart => { - }, - CaptureEvent::ISREnd => { - }, - CaptureEvent::End => { - // end the last atomic block - if let Some(&l) = last { - let start = trace[l].edge.1.unwrap(); - let end = trace[i].edge.0.unwrap(); - match abbs_of_task.get_mut(&curr_name) { - Some(v) => { - match v.iter_mut().find(|x| x.start==start) { - Some(abb) => { - Rc::get_mut(abb).unwrap().ends.insert(end); - } - None => { - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - } - }; - }, - None => { - let mut v = Vec::new(); - let mut t = HashSet::new(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start, ends: t})); - abbs_of_task.insert(curr_name, v); - } - } - } else { - // first API call of this task - let mut v = Vec::new(); - let mut t = HashSet::new(); - let end = trace[i].edge.0.unwrap(); - t.insert(end); - v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); - abbs_of_task.insert(curr_name, v); - } - }, - CaptureEvent::Undefined => { - }, - } - } - abbs_of_task -} +// fn extract_abbs_from_trace(trace: &Vec) -> HashMap>> { +// let mut abbs_of_task : HashMap>> = HashMap::new(); +// let mut last_abb_of_task : HashMap = HashMap::new(); +// // iterate over all states and extract atomic basic blocks +// // an atomic base block has a single entry and multiple exits +// // the cuts between blocks are api calls +// // when capture_point is APIEnd, the destination of the edge is the start of an atomic block +// // so the next APIStart with the same current_tcb.pcTaskName is the end of the atomic block +// for i in 0..trace.len() { +// let curr_name = trace[i].current_task.0.task_name.clone(); +// let last : Option<&usize> = last_abb_of_task.get(&curr_name); +// match trace[i].capture_point.0 { +// CaptureEvent::APIStart => { +// // end the last atomic block +// if let Some(&l) = last { +// let start = trace[l].edge.1.unwrap(); +// let end = trace[i].edge.0.unwrap(); +// match abbs_of_task.get_mut(&curr_name) { +// Some(v) => { +// match v.iter_mut().find(|x| x.start==start) { +// Some(abb) => { +// Rc::get_mut(abb).unwrap().ends.insert(end); +// } +// None => { +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// } +// }; +// }, +// None => { +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// } +// } else { +// // first API call of this task +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// let end = trace[i].edge.0.unwrap(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// }, +// CaptureEvent::APIEnd => { +// match last { +// Some(&l) => { +// //assert!(trace[l].capture_point.0 == CaptureEvent::APIStart); +// }, +// None => (), +// } +// last_abb_of_task.insert(curr_name, i); +// }, +// CaptureEvent::ISRStart => { +// }, +// CaptureEvent::ISREnd => { +// }, +// CaptureEvent::End => { +// // end the last atomic block +// if let Some(&l) = last { +// let start = trace[l].edge.1.unwrap(); +// let end = trace[i].edge.0.unwrap(); +// match abbs_of_task.get_mut(&curr_name) { +// Some(v) => { +// match v.iter_mut().find(|x| x.start==start) { +// Some(abb) => { +// Rc::get_mut(abb).unwrap().ends.insert(end); +// } +// None => { +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// } +// }; +// }, +// None => { +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// } +// } else { +// // first API call of this task +// let mut v = Vec::new(); +// let mut t = HashSet::new(); +// let end = trace[i].edge.0.unwrap(); +// t.insert(end); +// v.push(Rc::new(AtomicBasicBlock {start: 0, ends: t})); +// abbs_of_task.insert(curr_name, v); +// } +// }, +// CaptureEvent::Undefined => { +// }, +// } +// } +// abbs_of_task +// } /// returns (name, abb, index, ticks, Option) -fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { - let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time - let mut has_started : HashSet = HashSet::new(); - let mut abb_begin_end : HashMap = HashMap::new(); - let mut last_abb_of_task : HashMap = HashMap::new(); - for i in 0..trace.len() { - let curr_name = trace[i].current_task.0.task_name.clone(); - let last : Option<&usize> = last_abb_of_task.get(&curr_name); - match trace[i].capture_point.0 { - CaptureEvent::APIStart => { - // end the last atomic block, which began with APIEnd or initial PendSV End - if let Some(&l) = last { - // let start = trace[l].edge.1.unwrap(); - // let end = trace[i].edge.0.unwrap(); - abb_begin_end.insert(l, i); - } else { - panic!("Start an API call with no ABB to terminate"); - } - last_abb_of_task.remove(&curr_name); - }, - CaptureEvent::APIEnd => { - // APIEnd means a new ABB begins - match last { - Some(&l) => { - panic!("End an API call with open ABB"); - }, - None => (), - } - last_abb_of_task.insert(curr_name, i); - }, - CaptureEvent::ISRStart => { - }, - CaptureEvent::ISREnd => { - if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { - // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled - last_abb_of_task.insert(curr_name.clone(), i); - has_started.insert(curr_name); - } - }, - CaptureEvent::End => { - // end the last atomic block - if let Some(&l) = last { - abb_begin_end.insert(l, i); - } else { - panic!("End without running ABB"); - } - last_abb_of_task.remove(&curr_name); - }, - CaptureEvent::Undefined => { - }, - } - } - let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); - abb_intervals.sort_by_key(|(x,_)| *x); - let mut chunks = Vec::new(); // (name, abb, index, ticks) - for &(s,e) in abb_intervals.iter() { - let curr_name = trace[s].current_task.0.task_name.clone(); - let start = match trace[s].capture_point.0 { - CaptureEvent::ISREnd => 0, - CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), - _ => panic!(), - }; - let end = trace[e].edge.0.unwrap(); - let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); - // find intervalls where the abb is actually running, not preempted - // count up exec time - let mut sum_of_pieces = 0; - for i in s..e { - if trace[i].current_task.0.task_name == curr_name { - match trace[i].capture_point.0 { - CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - CaptureEvent::ISRStart => (), - CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, - _ => panic!(), - } - } - } - abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); - } - abbs_in_exec_order.sort_by_key(|x| x.0); - let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); - chunks.sort_by_key(|x| x.2); - (chunks, abbs_in_exec_order) -} +// fn trace_to_state_abb(trace: &Vec) -> (Vec<(String, Rc, usize, u64)>, Vec<(AtomicBasicBlock, u64)>) { +// let mut abbs_in_exec_order : Vec<(usize,AtomicBasicBlock,u64)> = vec![]; // indices in trace where an abb ends, along with it's time +// let mut has_started : HashSet = HashSet::new(); +// let mut abb_begin_end : HashMap = HashMap::new(); +// let mut last_abb_of_task : HashMap = HashMap::new(); +// for i in 0..trace.len() { +// let curr_name = trace[i].current_task.0.task_name.clone(); +// let last : Option<&usize> = last_abb_of_task.get(&curr_name); +// match trace[i].capture_point.0 { +// CaptureEvent::APIStart => { +// // end the last atomic block, which began with APIEnd or initial PendSV End +// if let Some(&l) = last { +// // let start = trace[l].edge.1.unwrap(); +// // let end = trace[i].edge.0.unwrap(); +// abb_begin_end.insert(l, i); +// } else { +// panic!("Start an API call with no ABB to terminate"); +// } +// last_abb_of_task.remove(&curr_name); +// }, +// CaptureEvent::APIEnd => { +// // APIEnd means a new ABB begins +// match last { +// Some(&l) => { +// panic!("End an API call with open ABB"); +// }, +// None => (), +// } +// last_abb_of_task.insert(curr_name, i); +// }, +// CaptureEvent::ISRStart => { +// }, +// CaptureEvent::ISREnd => { +// if last.is_none() && trace[i].capture_point.1=="xPortPendSVHandler" && !has_started.contains(&curr_name) { +// // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled +// last_abb_of_task.insert(curr_name.clone(), i); +// has_started.insert(curr_name); +// } +// }, +// CaptureEvent::End => { +// // end the last atomic block +// if let Some(&l) = last { +// abb_begin_end.insert(l, i); +// } else { +// panic!("End without running ABB"); +// } +// last_abb_of_task.remove(&curr_name); +// }, +// CaptureEvent::Undefined => { +// }, +// } +// } +// let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); +// abb_intervals.sort_by_key(|(x,_)| *x); +// let mut chunks = Vec::new(); // (name, abb, index, ticks) +// for &(s,e) in abb_intervals.iter() { +// let curr_name = trace[s].current_task.0.task_name.clone(); +// let start = match trace[s].capture_point.0 { +// CaptureEvent::ISREnd => 0, +// CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), +// _ => panic!(), +// }; +// let end = trace[e].edge.0.unwrap(); +// let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); +// // find intervalls where the abb is actually running, not preempted +// // count up exec time +// let mut sum_of_pieces = 0; +// for i in s..e { +// if trace[i].current_task.0.task_name == curr_name { +// match trace[i].capture_point.0 { +// CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, +// CaptureEvent::ISRStart => (), +// CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, +// _ => panic!(), +// } +// } +// } +// abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); +// } +// abbs_in_exec_order.sort_by_key(|x| x.0); +// let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); +// chunks.sort_by_key(|x| x.2); +// (chunks, abbs_in_exec_order) +// } libafl_bolts::impl_serdeany!(AtomicBasicBlock); \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index f57089689e..95025334fb 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -7,14 +7,18 @@ use libafl_bolts::AsSlice; use libafl::Error; use libafl::observers::Observer; use serde::{Deserialize, Serialize}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use crate::systemstate::CaptureEvent; +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::VecDeque; +use super::{ AtomicBasicBlock, ExecInterval}; use super::{ CURRENT_SYSTEMSTATE_VEC, RawFreeRTOSSystemState, RefinedTCB, - RefinedFreeRTOSSystemState, + ReducedFreeRTOSSystemState, freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*}, }; @@ -26,7 +30,9 @@ use super::{ #[allow(clippy::unsafe_derive_deserialize)] pub struct QemuSystemStateObserver { - pub last_run: Vec, + pub last_run: Vec, + pub last_states: HashMap, + pub last_trace: Vec, pub last_input: Vec, name: String, } @@ -44,7 +50,20 @@ where #[inline] fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { - unsafe {self.last_run = remove_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} + // unsafe {self.last_run = invalidate_ineffective_isr(refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC));} + unsafe { + let mut temp = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC); + self.last_run = temp.0.clone(); + // println!("{:?}",temp); + let mut temp = states2intervals(temp.0, temp.1); + self.last_trace = temp.0; + self.last_states = temp.1; + // println!("{:?}",temp); + } + // let abbs = extract_abbs_from_trace(&self.last_run); + // println!("{:?}",abbs); + // let abbs = trace_to_state_abb(&self.last_run); + // println!("{:?}",abbs); self.last_input=_input.target_bytes().as_slice().to_owned(); Ok(()) } @@ -68,7 +87,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver { pub fn new() -> Self { - Self{last_run: vec![], last_input: vec![], name: "systemstate".to_string()} + Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() } } } @@ -112,10 +131,8 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret } /// Drains a List of raw SystemStates to produce a refined trace -fn refine_system_states(input: &mut Vec) -> Vec { - let mut iteration_counts : HashMap= HashMap::new(); - let mut ret = Vec::::new(); - let mut start_tick : u64 = 0; +fn refine_system_states(input: &mut Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (Option, Option))>) { + let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); // collect ready list @@ -130,66 +147,339 @@ fn refine_system_states(input: &mut Vec) -> Vec = delay_list.into_iter().map(|x| {let t = *iteration_counts.get(&x.task_name).unwrap_or(&0); (x, t)}).collect(); - let t = *iteration_counts.get(&cur.task_name).unwrap_or(&1); - // We don't care about the order - ret.push(RefinedFreeRTOSSystemState { - current_task: (cur, t), - start_tick: start_tick, - end_tick: i.qemu_tick, + ret.0.push(ReducedFreeRTOSSystemState { + current_task: cur, ready_list_after: collector, delay_list_after: delay_list, - input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, - edge: i.edge, - capture_point: (i.capture_point.0,i.capture_point.1.to_string()), + // input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER, }); - start_tick=i.qemu_tick; + ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge)); } return ret; } -fn remove_ineffective_isr(mut trace: Vec) -> Vec { - // remove subsequent pairs of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. - let mut ret : Vec = Vec::new(); - ret.push(trace[0].clone()); - let mut i = 1; - while i < trace.len() - 1 { - if trace[i] == trace[i + 1] && - matches!(trace[i].capture_point.0, CaptureEvent::ISRStart) && - matches!(trace[i + 1].capture_point.0, CaptureEvent::ISREnd) && - trace[i].capture_point.1 == trace[i + 1].capture_point.1 - { - // extend the end of the last ABB until the end of the next one - ret.last_mut().unwrap().end_tick = trace[i+1].end_tick; +/// Transform the states and metadata into a list of ExecIntervals +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { + let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app - i+=2; - } else { - ret.push(trace[i].clone()); - i+=1; + + let mut level_of_task : HashMap<&str, u8> = HashMap::new(); + + let mut ret: Vec = vec![]; + let mut edges: Vec<(Option, Option)> = vec![]; + let mut last_hash : u64 = trace[0].get_hash(); + let mut table : HashMap = HashMap::new(); + table.insert(last_hash, trace[0].clone()); + for i in 0..trace.len()-1 { + let curr_name = trace[i].current_task.task_name.as_str(); + let level = match meta[i].1 { + CaptureEvent::APIEnd => { // API end always exits towards the app + *level_of_task.get_mut(curr_name).unwrap()=0; + &0 + }, + CaptureEvent::APIStart => { // API start can only be called in the app + *level_of_task.get_mut(curr_name).unwrap()=1; + &1 + }, + CaptureEvent::ISREnd => { + // special case where the next block is an app start + if !level_of_task.contains_key(curr_name) { + level_of_task.insert(curr_name, 0); + } + // nested isr, TODO: Test level > 2 + if isr_stack.len() > 1 { + isr_stack.pop_back(); + isr_stack.back().unwrap() + } else { + isr_stack.pop_back(); + // possibly go back to an api call that is still running for this task + level_of_task.get(curr_name).unwrap() + } + }, + CaptureEvent::ISRStart => { + if isr_stack.len() > 0 { + let l = isr_stack.back().unwrap(); + isr_stack.push_back(l+1); + isr_stack.back().unwrap() + } else { + isr_stack.push_back(2); + &2 + } + } + _ => &100 + }; + // if trace[i].2 == CaptureEvent::End {break;} + let next_hash=trace[i+1].get_hash(); + if !table.contains_key(&next_hash) { + table.insert(next_hash, trace[i+1].clone()); + } + ret.push(ExecInterval{ + start_tick: meta[i].0, + end_tick: meta[i+1].0, + start_state: last_hash, + end_state: next_hash, + start_capture: (meta[i].1, meta[i].2.clone()), + end_capture: (meta[i+1].1, meta[i+1].2.clone()), + level: *level, + tick_spend_preempted: 0, + abb: None + }); + last_hash = next_hash; + edges.push((meta[i].3.1, meta[i+1].3.0)); + } + add_abb_info(&mut ret, &table, &edges); + (ret, table) +} + +fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(Option, Option)>) { + let mut task_has_started : HashSet = HashSet::new(); + let mut wip_abb_trace : Vec>> = vec![]; + let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); + + for i in 0..trace.len() { + let curr_name = &table[&trace[i].start_state].current_task.task_name; + // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); + + let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); // apps/apis are differentiated by task name, isrs by nested level + + match (&trace[i].start_capture.0, &trace[i].end_capture.0) { + // case with abb to block correspondence + // APP ABB + (CaptureEvent::APIEnd , CaptureEvent::APIStart ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::APIEnd , CaptureEvent::End ) => {assert_eq!(trace[i].level,0); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // API ABB + (CaptureEvent::APIStart, CaptureEvent::APIEnd ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::APIStart , CaptureEvent::End ) => {assert_eq!(trace[i].level,1); assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // ISR ABB + (CaptureEvent::ISRStart, CaptureEvent::ISREnd ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + (CaptureEvent::ISRStart , CaptureEvent::End ) => {assert_eq!(open_abb, None); wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::from([edges[i].1.unwrap()]), level: if trace[i].level<2 {trace[i].level} else {2}})))}, + // + (_, _) => { + match trace[i].start_capture.0 { + // generic api abb start + CaptureEvent::APIStart => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic isr abb start + CaptureEvent::ISRStart => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic app abb start + CaptureEvent::APIEnd => { + assert_eq!(open_abb, None); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0.unwrap(), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + }, + // generic continued blocks + CaptureEvent::ISREnd => { + // special case app abb start + if trace[i].start_capture.1=="xPortPendSVHandler" && !task_has_started.contains(curr_name) { + assert_eq!(open_abb, None); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: 0, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))); + open_abb_at_this_task_or_level.insert((trace[i].level, if trace[i].level<2 {&curr_name} else {""}), i); + task_has_started.insert(curr_name.clone()); + } else { + // generic case, continue a preempted block + let last = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).expect(&format!("Continued block with no start {} {} {}", trace[i].start_tick, task_has_started.contains(curr_name),trace[i].level)); + wip_abb_trace.push(wip_abb_trace[*last].clone()); + } + }, + _ => panic!("Undefined block start") + } + match trace[i].end_capture.0 { + // generic app abb end + CaptureEvent::APIStart => { + let _t = &wip_abb_trace[i]; + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic api abb end + CaptureEvent::APIEnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // generic isr abb end + CaptureEvent::ISREnd => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + // end anything + CaptureEvent::End => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1.unwrap()); + open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})); + }, + CaptureEvent::ISRStart => (), + _ => panic!("Undefined block end") + } + } + } + // println!("{} {:x}-{:x} {:x} {} {} {:?} {:?}",curr_name, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, &table[&trace[i].end_state].current_task.task_name, trace[i].level, trace[i].start_capture, trace[i].end_capture); + } + + for i in 0..trace.len() { + trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); + } + // let last : Option<&usize> = last_abb_start_of_task.get(&curr_name); + // // genric start of an ABB that gets preeempted + // match trace[i].start_capture.0 { + // CaptureEvent::APIEnd => { + // if last.is_some() { + // panic!("End an API call with open ABB"); + // } + // last_abb_start_of_task.insert(curr_name, i); + // } + // CaptureEvent::ISREnd => { + // if last.is_none() && trace[i].end_capture.1=="xPortPendSVHandler" && !task_has_started.contains(&curr_name) { + // // The initial ABB of a tasks starts not when an api call ends, but when it is fist scheduled + // last_abb_start_of_task.insert(curr_name.clone(), i); + // task_has_started.insert(curr_name); + // } + // }, + // _ => (), + // } + // // genric end of an ABB that got preeempted + // match trace[i].end_capture.0 { + // CaptureEvent::APIStart => { + // if let Some(&l) = last { + // // let start = trace[l].edge.1.unwrap(); + // // let end = trace[i].edge.0.unwrap(); + // abb_begin_end.insert(l, i); + // } else { + // panic!("Start an API call with no ABB to terminate"); + // } + // last_abb_start_of_task.remove(&curr_name); + // } + // CaptureEvent::End => { + // if let Some(&l) = last { + // // let start = trace[l].edge.1.unwrap(); + // // let end = trace[i].edge.0.unwrap(); + // abb_begin_end.insert(l, i); + // last_abb_start_of_task.remove(&curr_name); + // } else { + // eprintln!("End with no ABB to terminate"); // could happen if the run ends in a kernel panic + // } + // } + // _ => (), + // } + // } + // } + + // for (b,e) in abb_begin_end.into_iter() { + // let curr_name = table[&trace[i].start_state].current_task.task_name.clone(); + // let abb = Some(AtomicBasicBlock{start: edges[b].0.unwrap(), ends: HashSet::from([edges[e].1.unwrap()])}); + // for j in b..(e+1) { + // if trace[j].current_task.0.task_name == curr_name { + // trace[j].abb=abb.clone(); + // } + // } + // } + + // let mut abb_intervals : Vec<_> = abb_begin_end.into_iter().collect(); + // abb_intervals.sort_by_key(|(x,_)| *x); + // let mut chunks = Vec::new(); // (name, abb, index, ticks) + // for &(s,e) in abb_intervals.iter() { + // let curr_name = trace[s].current_task.0.task_name.clone(); + // let start = match trace[s].capture_point.0 { + // CaptureEvent::ISREnd => 0, + // CaptureEvent::APIEnd => trace[s].edge.1.unwrap(), + // _ => panic!(), + // }; + // let end = trace[e].edge.0.unwrap(); + // let abb = Rc::new(AtomicBasicBlock {start, ends: HashSet::from([end])}); + // // find intervalls where the abb is actually running, not preempted + // // count up exec time + // let mut sum_of_pieces = 0; + // for i in s..e { + // if trace[i].current_task.0.task_name == curr_name { + // match trace[i].capture_point.0 { + // CaptureEvent::APIEnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, + // CaptureEvent::ISRStart => (), + // CaptureEvent::ISREnd => {chunks.push((curr_name.clone(), abb.clone(), i, trace[i+1].end_tick-trace[i].end_tick)); sum_of_pieces+=trace[i+1].end_tick-trace[i].end_tick;}, + // _ => panic!(), + // } + // } + // } + // abbs_in_exec_order.push((e,(*abb).clone(), sum_of_pieces)); + // } + // abbs_in_exec_order.sort_by_key(|x| x.0); + // let abbs_in_exec_order : Vec<_> = abbs_in_exec_order.into_iter().map(|(_x,y,z)| (y,z)).collect(); + // chunks.sort_by_key(|x| x.2); + // (chunks, abbs_in_exec_order) +} + + +/// invalidate subsequent intervals of equal states where an ISREnd follows an ISRStart. If the interrupt had no effect on the system we, are not interested. +fn invalidate_ineffective_isr(trace: &mut Vec) { + let mut i = 0; + while i < trace.len() - 1 { + if trace[i].is_valid() && + matches!(trace[i].start_capture.0, CaptureEvent::ISRStart) && matches!(trace[i].end_capture.0, CaptureEvent::ISREnd) && + trace[i].start_capture.1 == trace[i].end_capture.1 && trace[i].start_state == trace[i].end_state + { + trace[i].invaildate(); } } - ret } + +/// merge a sequence of intervals of the same state+abb. jump over all invalid blocks. +fn merge_subsequent_abbs(trace: &mut Vec) { + let mut i = 1; + let mut lst_valid=0; + while i < trace.len() - 1 { + if trace[i].is_valid() { + let mut temp = trace[i].clone(); + trace[lst_valid].try_unite_with_later_interval(&mut temp); + trace[i] = temp; + lst_valid = i; + } + } +} + +//============================= ABB Observer + +// The Qemusystemstate Observer retrieves the systemstate +// that will get updated by the target. +// #[derive(Serialize, Deserialize, Debug, Default)] +// #[allow(clippy::unsafe_derive_deserialize)] +// pub struct ABBObserver +// { +// pub last_run: HashMap>, +// name: String, +// } + +// impl Observer for ABBObserver +// where +// S: UsesInput, +// S::Input : HasTargetBytes, +// { +// #[inline] +// fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { +// self.last_run.clear(); +// Ok(()) +// } + +// #[inline] +// fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> { +// unsafe {self.last_run=extract_abbs_from_trace(&mut CURRENT_SYSTEMSTATE_VEC);} +// println!("{:?}", self.last_run); +// Ok(()) +// } +// } + +// impl Named for ABBObserver +// { +// #[inline] +// fn name(&self) -> &str { +// self.name.as_str() +// } +// } + +// impl ABBObserver { +// pub fn new() -> Self { +// Self{last_run: HashMap::new(), name: "abbs".to_string()} +// } +// } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/stg.rs b/fuzzers/FRET/src/systemstate/stg.rs index ced853e783..409a2c49e7 100644 --- a/fuzzers/FRET/src/systemstate/stg.rs +++ b/fuzzers/FRET/src/systemstate/stg.rs @@ -45,9 +45,10 @@ use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, stat use serde::{Deserialize, Serialize}; use super::feedbacks::SystemStateFeedbackState; -use super::trace_to_state_abb; use super::AtomicBasicBlock; -use super::RefinedFreeRTOSSystemState; +use super::CaptureEvent; +use super::ExecInterval; +use super::ReducedFreeRTOSSystemState; use super::FreeRTOSSystemStateMetadata; use super::observers::QemuSystemStateObserver; use petgraph::prelude::DiGraph; @@ -62,12 +63,12 @@ use libafl_bolts::rands::Rand; #[derive(Serialize, Deserialize, Clone, Debug, Default, Hash)] pub struct STGNode { - base: RefinedFreeRTOSSystemState, + base: ReducedFreeRTOSSystemState, abb: AtomicBasicBlock, } impl STGNode { pub fn pretty_print(&self) -> String { - format!("{}\n{:x}-{:x}\n{}", self.base.current_task.0.task_name, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0), self.base.print_lists()) + format!("{}\nl{} {:x}-{:x}\n{}", self.base.current_task.task_name, self.abb.level, self.abb.start, self.abb.ends.iter().next().unwrap_or_else(||&0xFFFF), self.base.print_lists()) } fn calculate_hash(&self) -> u64 { let mut s = DefaultHasher::new(); @@ -82,13 +83,37 @@ impl PartialEq for STGNode { } } +#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash, PartialEq, Eq)] +pub struct STGEdge +{ + // is_interrupt: bool, + event: CaptureEvent, + name: String +} + +impl STGEdge { + pub fn pretty_print(&self) -> String { + let mut short = match self.event { + CaptureEvent::APIStart => "Call: ", + CaptureEvent::APIEnd => "Ret: ", + CaptureEvent::ISRStart => "Int: ", + CaptureEvent::ISREnd => "IRet: ", + CaptureEvent::End => "End: ", + CaptureEvent::Undefined => "", + }.to_string(); + short.push_str(&self.name); + short + } +} + /// Shared Metadata for a systemstateFeedback #[derive(Debug, Serialize, Deserialize, SerdeAny, Clone)] pub struct STGFeedbackState { // aggregated traces as a graph - pub graph: DiGraph, - index: HashMap, + pub graph: DiGraph, + systemstate_index: HashMap, + stgnode_index: HashMap, entrypoint: NodeIndex, exit: NodeIndex, // Metadata about aggregated traces. aggegated meaning, order has been removed @@ -99,9 +124,11 @@ impl Default for STGFeedbackState { fn default() -> STGFeedbackState { let mut graph = DiGraph::new(); let mut entry = STGNode::default(); - entry.base.current_task.0.task_name="Start".to_string(); + entry.base.current_task.task_name="Start".to_string(); let mut exit = STGNode::default(); - exit.base.current_task.0.task_name="End".to_string(); + exit.base.current_task.task_name="End".to_string(); + + let systemstate_index = HashMap::from([(entry.base.get_hash(), entry.base.clone()), (exit.base.get_hash(), exit.base.clone())]); let h_entry = entry.calculate_hash(); let h_exit = exit.calculate_hash(); @@ -113,10 +140,11 @@ impl Default for STGFeedbackState { STGFeedbackState { graph, - index, + stgnode_index: index, entrypoint, exit, worst_observed_per_aggegated_path: HashMap::new(), + systemstate_index, } } } @@ -181,47 +209,103 @@ impl StgFeedback { /// newly discovered node? /// side effect: /// the graph gets new nodes - fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + // fn update_stg(trace: &Vec, map: Vec<(String, Rc, usize, u64)>, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { + // let mut return_node_trace = vec![fbs.entrypoint]; + // let mut return_edge_trace = vec![]; + // let mut interesting = false; + // // add all missing state+abb combinations to the graph + // for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact + // if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // + // let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; + // let h_node = node.calculate_hash(); + // let next_idx = if let Some(idx) = fbs.index.get(&h_node) { + // // alredy present + // *idx + // } else { + // // not present + // let idx = fbs.graph.add_node(node); + // fbs.index.insert(h_node, idx); + // interesting |= INTEREST_NODE; + // idx + // }; + // // connect in graph if edge not present + // let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); + // if let Some(e_) = e { + // return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); + // } else { + // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); + // return_edge_trace.push(e_); + // interesting |= INTEREST_EDGE; + // } + // return_node_trace.push(next_idx); + // /* + // Ideas: + // Mark edges triggered by interrupts + // Specify path with edges instead of nodes? + // Form a coverage map over edges? + // Sum up execution time per ABB + // */ + // } + // } + // // every path terminates at the end + // if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { + // let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); + // return_edge_trace.push(e_); + // interesting |= INTEREST_EDGE; + // } + // return_node_trace.push(fbs.exit); + // #[cfg(feature = "feed_stg")] + // set_observer_map(&return_edge_trace); + // (return_node_trace, return_edge_trace, interesting) + // } + + /// params: + /// tarce of system states + /// list of all atomic basic blocks with asociated state index and ticks + /// produces: + /// tarce of node indexes representing the path trough the graph + /// newly discovered node? + /// side effect: + /// the graph gets new nodes + fn update_stg_interval(trace: &Vec, table: &HashMap, fbs: &mut STGFeedbackState) -> (Vec, Vec, bool) { let mut return_node_trace = vec![fbs.entrypoint]; let mut return_edge_trace = vec![]; let mut interesting = false; // add all missing state+abb combinations to the graph - for (i,state) in trace.iter().enumerate() { // Iterate states first, keep the trace order intact - if let Some(marker) = map.iter().filter(|(_,_,index,_)| index==&i).next() { // - let node = STGNode {base: state.clone(), abb: (*marker.1).clone()}; - let h_node = node.calculate_hash(); - let next_idx = if let Some(idx) = fbs.index.get(&h_node) { - // alredy present - *idx - } else { - // not present - let idx = fbs.graph.add_node(node); - fbs.index.insert(h_node, idx); - interesting |= INTEREST_NODE; - idx - }; - // connect in graph if edge not present - let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); - if let Some(e_) = e { - return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); - } else { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, ()); - return_edge_trace.push(e_); - interesting |= INTEREST_EDGE; - } - return_node_trace.push(next_idx); - /* - Ideas: - Mark edges triggered by interrupts - Specify path with edges instead of nodes? - Form a coverage map over edges? - Sum up execution time per ABB - */ + for (i,interval) in trace.iter().enumerate() { // Iterate intervals + let node = STGNode {base: table[&interval.start_state].clone(), abb: interval.abb.as_ref().unwrap().clone()}; + let h_node = node.calculate_hash(); + let next_idx = if let Some(idx) = fbs.stgnode_index.get(&h_node) { + // alredy present + *idx + } else { + // not present + let idx = fbs.graph.add_node(node); + fbs.stgnode_index.insert(h_node, idx); + interesting |= INTEREST_NODE; + idx + }; + // connect in graph if edge not present + let e = fbs.graph.edges_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).find(|x| petgraph::visit::EdgeRef::target(x) == next_idx); + if let Some(e_) = e { + return_edge_trace.push(petgraph::visit::EdgeRef::id(&e_)); + } else { + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], next_idx, STGEdge{event: interval.start_capture.0, name: interval.start_capture.1.clone()}); + return_edge_trace.push(e_); + interesting |= INTEREST_EDGE; } + return_node_trace.push(next_idx); + /* + Ideas: + Mark edges triggered by interrupts + Specify path with edges instead of nodes? + Form a coverage map over edges? + Sum up execution time per ABB + */ } // every path terminates at the end if !fbs.graph.neighbors_directed(return_node_trace[return_node_trace.len()-1],Direction::Outgoing).any(|x| x == fbs.exit) { - let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, ()); + let e_ = fbs.graph.add_edge(return_node_trace[return_node_trace.len()-1], fbs.exit, STGEdge { event: CaptureEvent::End, name: String::from("End") }); return_edge_trace.push(e_); interesting |= INTEREST_EDGE; } @@ -265,31 +349,33 @@ where } }; - let (abbs, ordered) = trace_to_state_abb(&observer.last_run); - // println!("{:?}",abbs); - let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); - if INTEREST_AGGREGATE { - let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); - // aggegation by sorting, order of states is not relevant - let mut tmp : Vec<_> = it1; - tmp.sort(); - if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { - let t = clock_observer.last_runtime(); - if t > *x { - *x = t; - interesting |= true; - } - } else { - feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); - interesting |= true; - } - } + let (_trace, _, mut interesting) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_states, feedbackstate); + // let (abbs, ordered) = trace_to_state_abb(&observer.last_run); + // // println!("{:?}",abbs); + // let (_trace, _, mut interesting) = StgFeedback::update_stg(&observer.last_run, abbs, feedbackstate); + // if INTEREST_AGGREGATE { + // let (it1, _it2) : (Vec<_>, Vec<_>) = ordered.into_iter().unzip(); + // // aggegation by sorting, order of states is not relevant + // let mut tmp : Vec<_> = it1; + // tmp.sort(); + // if let Some(x) = feedbackstate.worst_observed_per_aggegated_path.get_mut(&tmp) { + // let t = clock_observer.last_runtime(); + // if t > *x { + // *x = t; + // interesting |= INTEREST_AGGREGATE; + // } + // } else { + // feedbackstate.worst_observed_per_aggegated_path.insert(tmp, clock_observer.last_runtime()); + // interesting |= INTEREST_AGGREGATE; + // } + // } // let out = feedbackstate.graph.map(|i,x| x.pretty_print(), |_,_| ""); // let outs = Dot::with_config(&out, &[Config::EdgeNoLabel]).to_string(); // let outs = outs.replace(';',"\\n"); // fs::write("./mystg.dot",outs).expect("Failed to write graph"); + // Ok(interesting) Ok(interesting) }