From 8417613cb28254e137943e982611217e767dce1b Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 21 Oct 2024 17:13:38 +0200 Subject: [PATCH] save stats per abb --- fuzzers/FRET/benchmark/plot_all_traces.sh | 2 +- fuzzers/FRET/src/systemstate/feedbacks.rs | 25 +++++++++++++++++- fuzzers/FRET/src/systemstate/mod.rs | 22 +++++++++++++--- fuzzers/FRET/src/systemstate/observers.rs | 31 ++++++++++++++++++----- fuzzers/FRET/src/time/clock.rs | 4 +++ 5 files changed, 73 insertions(+), 11 deletions(-) diff --git a/fuzzers/FRET/benchmark/plot_all_traces.sh b/fuzzers/FRET/benchmark/plot_all_traces.sh index a07b7e7e30..27e2575f09 100644 --- a/fuzzers/FRET/benchmark/plot_all_traces.sh +++ b/fuzzers/FRET/benchmark/plot_all_traces.sh @@ -25,4 +25,4 @@ do done < <(find ./remote/timedump -maxdepth 2 -type 'f' -iregex '.*\.case') # echo "${PLOTS[@]}" -snakemake -c 6 "${PLOTS[@]}" \ No newline at end of file +snakemake -c 6 --keep-incomplete "${PLOTS[@]}" diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 35c9748d03..132b5a0468 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -192,7 +192,30 @@ where let names : Vec = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect(); match &self.dumpfile { Some(s) => { - std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances)).expect("Error serializing hashmap")).expect("Can not dump to file"); + let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.2.clone()) == observer.select_task).max_by(|a,b| (a.1-a.0).cmp(&(b.1-b.0))) { + // extract computation time spent in each task and abb + let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect(); + // task_name -> addr -> (count, time) + let mut ret : HashMap> = HashMap::new(); + let mut t2 = t.clone(); + t2.sort_by_key(|x| x.get_task_name_unchecked()); + t2.chunk_by_mut(|x,y| x.get_task_name_unchecked() == y.get_task_name_unchecked()).for_each(|x| { + x.sort_by_key(|y| y.abb.as_ref().unwrap().start); + x.chunk_by(|y,z| y.abb.as_ref().unwrap().start == z.abb.as_ref().unwrap().start).for_each(|y| { + match ret.get_mut(&y[0].get_task_name_unchecked()) { + Option::None => { + ret.insert(y[0].get_task_name_unchecked(), HashMap::from([(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum::<_>()))])); + } + Some(x) => { + x.insert(y[0].abb.as_ref().unwrap().start, (y.len(), y.iter().filter(|x| x.is_abb_end()).count(), y.iter().map(|z| z.get_exec_time()).sum())); + } + } + }); + }); + dbg!(&ret); + ret + } else {HashMap::new()}; + std::fs::write(s,ron::to_string(&(&observer.last_trace,&observer.last_states,&observer.job_instances,per_task_metadata)).expect("Error serializing hashmap")).expect("Can not dump to file"); self.dumpfile = None }, Option::None => if self.dump_metadata {println!("{:?}\n{:?}",observer.last_run,names);} diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 18b341662c..1c470ef866 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -194,6 +194,7 @@ pub struct ExecInterval { pub end_state: u64, pub start_capture: (CaptureEvent, String), pub end_capture: (CaptureEvent, String), + pub interval_name: String, pub level: u8, tick_spend_preempted: u64, pub abb: Option @@ -228,6 +229,20 @@ impl ExecInterval { pub fn get_hash_index(&self) -> (u64, u64) { return (self.start_state, self.abb.as_ref().expect("ABB not set").get_hash()) } + + pub fn get_task_name(&self) -> Option { + self.abb.as_ref().map(|x| x.instance_name.clone()).flatten() + } + pub fn get_task_name_unchecked(&self) -> String { + self.get_task_name().unwrap_or_else(|| "unknown".to_string()) + } + + pub fn is_abb_end(&self) -> bool { + match self.end_capture.0 { + CaptureEvent::APIStart | CaptureEvent::APIEnd | CaptureEvent::ISREnd | CaptureEvent::End => true, + _ => false + } + } } // Wrapper around Vec to attach as Metadata @@ -280,7 +295,8 @@ pub struct AtomicBasicBlock { start: GuestAddr, ends: HashSet, level: u8, - instance_id: usize + instance_id: usize, + instance_name: Option, } impl PartialEq for AtomicBasicBlock { @@ -308,7 +324,7 @@ impl fmt::Display for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("0x{:#x}, ", end)); } - write!(f, "ABB {{ level: {}, start: 0x{:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {} {{ level: {}, start: 0x{:#x}, ends: [{}]}}", &self.instance_name.as_ref().unwrap_or(&"".to_string()), self.level, self.start, ends_str.trim().trim_matches(',')) } } impl fmt::Debug for AtomicBasicBlock { @@ -317,7 +333,7 @@ impl fmt::Debug for AtomicBasicBlock { for end in &self.ends { ends_str.push_str(&format!("{:#x}, ", end)); } - write!(f, "ABB {{ level: {}, start: {:#x}, ends: [{}]}}", self.level, self.start, ends_str.trim().trim_matches(',')) + write!(f, "ABB {} {{ level: {}, start: 0x{:#x}, ends: [{}]}}", &self.instance_name.as_ref().unwrap_or(&"".to_string()), self.level, self.start, ends_str.trim().trim_matches(',')) } } diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 72bb722caa..64dbe02346 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -83,7 +83,7 @@ where let observer = &self; let mut worst_case_per_task = HashMap::new(); observer.job_instances.iter().for_each(|x| { - let time = (x.1-x.0); + let time = x.1-x.0; if worst_case_per_task.get(&x.2).is_some() { let old = worst_case_per_task.get_mut(&x.2).unwrap(); if time > *old { @@ -191,7 +191,11 @@ fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap) -> ret.push(last_tcb); ret } + /// Drains a List of raw SystemStates to produce a refined trace +/// returns: +/// - a Vec of ReducedFreeRTOSSystemStates +/// - a Vec of metadata tuples (qemu_tick, capture_event, capture_name, edge, mem_reads) fn refine_system_states(mut input: Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { @@ -412,6 +416,11 @@ fn get_release_response_pairs(rel: &Vec<(u64, String)>, resp: &Vec<(u64, String) } /// Transform the states and metadata into a list of ExecIntervals, along with a HashMap of states, a list of HashSets marking memory reads and a bool indicating success +/// returns: +/// - a Vec of ExecIntervals +/// - a Vec of HashSets marking memory reads during these intervals +/// - a HashMap of ReducedFreeRTOSSystemStates by hash +/// - a bool indicating success fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32), HashSet)>) -> (Vec, Vec>, HashMap, bool) { if trace.len() == 0 {return (Vec::new(), Vec::new(), HashMap::new(), true);} let mut isr_stack : VecDeque = VecDeque::from([]); // 2+ = ISR, 1 = systemcall, 0 = APP. Trace starts with an ISREnd and executes the app @@ -427,6 +436,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt 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 mut interval_name = curr_name; // Name of the interval, either the task name or the isr/api funtion name let level = match meta[i].1 { CaptureEvent::APIEnd => { // API end always exits towards the app if !level_of_task.contains_key(curr_name) { @@ -440,6 +450,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt level_of_task.insert(curr_name, 0); } *level_of_task.get_mut(curr_name).unwrap()=1; + // interval_name = &meta[i].2; 1 }, CaptureEvent::ISREnd => { @@ -449,11 +460,15 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt } // nested isr, TODO: Test level > 2 if isr_stack.len() > 1 { + // interval_name = ""; // We can't know which isr is running isr_stack.pop_back().unwrap(); *isr_stack.back().unwrap() } else { isr_stack.pop_back(); // possibly go back to an api call that is still running for this task + if level_of_task.get(curr_name).unwrap() == &1 { + // interval_name = ""; // We can't know which api is running + } *level_of_task.get(curr_name).unwrap() } }, @@ -463,6 +478,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt // &2 // } else { // regular case + // interval_name = &meta[i].2; if isr_stack.len() > 0 { let l = *isr_stack.back().unwrap(); isr_stack.push_back(l+1); @@ -488,6 +504,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt start_capture: (meta[i].1, meta[i].2.clone()), end_capture: (meta[i+1].1, meta[i+1].2.clone()), level: level, + interval_name: String::new(), tick_spend_preempted: 0, abb: None }); @@ -499,6 +516,7 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt (ret, reads, table, t) } +/// Marks which abbs were executed at each interval fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { let mut id_count = 0; let mut ret = true; @@ -522,7 +540,7 @@ fn add_abb_info(trace: &mut Vec, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap, table: &HashMap Duration { Duration::from_nanos((ticks << QEMU_ICOUNT_SHIFT) as u64) } +pub fn tick_to_ms(ticks: u64) -> f32 { + (Duration::from_nanos(ticks << QEMU_ICOUNT_SHIFT).as_micros() as f32/10.0).round()/100.0 +} + //========== Metadata #[derive(Debug, SerdeAny, Serialize, Deserialize)] pub struct QemuIcountMetadata {