diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index b9e59a3342..91f2aa95a7 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -14,7 +14,7 @@ edges::{self, edges_map_mut_slice, QemuEdgeCoverageHelper, MAX_EDGES_NUM}, elf:: }; use rand::{SeedableRng, StdRng, Rng}; use crate::{ -clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME, input_bytes_to_interrupt_times}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, stg::{GraphMaximizerCorpusScheduler}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} +clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, MINIMUM_INTER_ARRIVAL_TIME}, qemustate::QemuStateRestoreHelper, systemstate::{self, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback, SystraceErrorFeedback}, helpers::{QemuSystemStateHelper, ISR_SYMBOLS}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeStateMaximizerCorpusScheduler} }; use std::time::{SystemTime, UNIX_EPOCH}; use clap::{Parser, Subcommand}; @@ -549,7 +549,7 @@ let mut run_client = |state: Option<_>, mut mgr, _core_id| { ); // A feedback to choose if an input is a solution or not - let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); + let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::new(cli.dump_cases)); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/FRET/src/mutational.rs b/fuzzers/FRET/src/mutational.rs index d020ef918d..4a3d088368 100644 --- a/fuzzers/FRET/src/mutational.rs +++ b/fuzzers/FRET/src/mutational.rs @@ -13,7 +13,7 @@ use libafl::{ corpus::{self, Corpus}, fuzzer::Evaluator, mark_feature_time, prelude::{new_hash_feedback, CorpusId, HasBytesVec, MutationResult, Mutator, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error }; use libafl::prelude::State; -use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; +use crate::{clock::{IcHist, QEMU_ISNS_PER_USEC}, fuzzer::{DO_NUM_INTERRUPT, FIRST_INT, MAX_NUM_INTERRUPT}, systemstate::{stg::{STGFeedbackState, STGNodeMetadata}, CaptureEvent, ExecInterval, FreeRTOSSystemStateMetadata, ReducedFreeRTOSSystemState}}; pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC; // one isn per 2**4 ns @@ -106,7 +106,7 @@ where } // produce a slice of absolute interrupt times - let mut interrupt_offsets : [u32; 32] = [u32::MAX; 32]; + let mut interrupt_offsets : [u32; MAX_NUM_INTERRUPT] = [u32::MAX; MAX_NUM_INTERRUPT]; let mut num_interrupts : usize = 0; { let t = input_bytes_to_interrupt_times(&target_bytes); diff --git a/fuzzers/FRET/src/systemstate/feedbacks.rs b/fuzzers/FRET/src/systemstate/feedbacks.rs index 8965277bce..b9a481da94 100644 --- a/fuzzers/FRET/src/systemstate/feedbacks.rs +++ b/fuzzers/FRET/src/systemstate/feedbacks.rs @@ -224,4 +224,60 @@ impl DumpSystraceFeedback pub fn metadata_only() -> Self { Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None} } +} + + +#[derive(Debug, Default)] +pub struct SystraceErrorFeedback +{ + dump_case: bool +} + +impl Feedback for SystraceErrorFeedback +where + S: State + UsesInput + MaybeHasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple + { + let observer = observers.match_name::("systemstate") + .expect("QemuSystemStateObserver not found"); + Ok(self.dump_case&&!observer.success) + } + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, _observers: &OT, _testcase: &mut Testcase) -> Result<(), Error> { + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for SystraceErrorFeedback +{ + #[inline] + fn name(&self) -> &str { + "SystraceErrorFeedback" + } +} + +impl SystraceErrorFeedback +{ + #[must_use] + pub fn new(dump_case: bool) -> Self { + Self {dump_case} + } } \ No newline at end of file diff --git a/fuzzers/FRET/src/systemstate/helpers.rs b/fuzzers/FRET/src/systemstate/helpers.rs index fa418dc8ce..759e19e98b 100644 --- a/fuzzers/FRET/src/systemstate/helpers.rs +++ b/fuzzers/FRET/src/systemstate/helpers.rs @@ -4,6 +4,7 @@ use std::ops::Range; use hashbrown::HashMap; use libafl::prelude::ExitKind; use libafl::prelude::UsesInput; +use libafl_qemu::read_user_reg_unchecked; use libafl_qemu::Emulator; use libafl_qemu::GuestAddr; use libafl_qemu::GuestPhysAddr; @@ -115,24 +116,22 @@ where for wp in self.isr_addrs.keys() { _hooks.instruction(*wp, Hook::Function(exec_isr_hook::), false); } - _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_api_call::)); + _hooks.jmps(Hook::Function(gen_jmp_is_syscall::), Hook::Function(trace_jmp::)); } // TODO: refactor duplicate code fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) { unsafe { CURRENT_SYSTEMSTATE_VEC.clear(); - let p = LAST_API_CALL.with(|x| x.get()); - *p = None; } } fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) { - trigger_collection(emulator,(None, None), self); + trigger_collection(emulator,(0, 0), CaptureEvent::End, self); unsafe { let c = emulator.cpu_from_index(0); let pc = c.read_reg::(15).unwrap(); - CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (Some(pc),None); + CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].edge = (pc,0); CURRENT_SYSTEMSTATE_VEC[CURRENT_SYSTEMSTATE_VEC.len()-1].capture_point = (CaptureEvent::End,"Breakpoint".to_string()); } // Find the first ISREnd of vPortSVCHandler and drop anything before @@ -188,62 +187,38 @@ fn read_freertos_list(systemstate : &mut RawFreeRTOSSystemState, emulator: &Emul } #[inline] -fn trigger_collection(emulator: &Emulator, edge: (Option,Option), h: &QemuSystemStateHelper) { +fn trigger_collection(emulator: &Emulator, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) { let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::()).unwrap(); let mut systemstate = RawFreeRTOSSystemState::default(); - // Note type of capture - match edge.1 { - None => { // No destination set, must be ISR Return - // ISR End - if let Some(src) = edge.0 { - if let Some(s) = h.isr_addrs.get(&src) { - systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string()); - } else { - println!("ISR Ret Not found: {:#x}", src); - systemstate.capture_point=(CaptureEvent::ISREnd, "".to_string()); - } - } + match event { + CaptureEvent::APIStart => { + let s = h.api_fn_addrs.get(&edge.1).unwrap(); + systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); }, - Some(dest) => { - if let Some(src) = edge.0 { // Both set, can be API Call/Ret - if let Some(s) = h.api_fn_addrs.get(&src) { // API End - systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string()); - if !h.app_range.contains(&dest) { - println!("API Not found: {:#x} {:#x}", src, dest); - } - } else if let Some(s) = h.api_fn_addrs.get(&dest) { // API Call - // if let None = in_any_range(&h.isr_ranges, src) { - systemstate.capture_point=(CaptureEvent::APIStart, s.to_string()); - // } else { - // return; - // } - } else { - println!("API Not found: {:#x}", src); - } - } else { // No source, must be ISR - if let Some(s) = h.isr_addrs.get(&dest) { // ISR Start - systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string()); - } else { - println!("ISR call Not found: {:#x}", dest); - } - } + CaptureEvent::APIEnd => { + let s = h.api_fn_addrs.get(&edge.0).unwrap(); + systemstate.capture_point=(CaptureEvent::APIEnd, s.to_string()); }, + CaptureEvent::ISRStart => { + let s = h.isr_addrs.get(&edge.1).unwrap(); + systemstate.capture_point=(CaptureEvent::ISRStart, s.to_string()); + }, + CaptureEvent::ISREnd => { + let s = h.isr_addrs.get(&edge.0).unwrap(); + systemstate.capture_point=(CaptureEvent::ISREnd, s.to_string()); + }, + CaptureEvent::End => {systemstate.capture_point=(CaptureEvent::End, "".to_string());}, + CaptureEvent::Undefined => (), } + if systemstate.capture_point.0 == CaptureEvent::Undefined { // println!("Not found: {:#x} {:#x}", edge.0.unwrap_or(0), edge.1.unwrap_or(0)); } - systemstate.edge = edge; + systemstate.edge = ((edge.0),(edge.1)); + systemstate.qemu_tick = get_icount(emulator); - unsafe { - // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround - let c = emulator.cpu_from_index(0); - let can_do_io = (*c.raw_ptr()).neg.can_do_io; - (*c.raw_ptr()).neg.can_do_io = true; - systemstate.qemu_tick = emu::icount_get_raw(); - (*c.raw_ptr()).neg.can_do_io = can_do_io; - } let mut buf : [u8; 4] = [0,0,0,0]; match h.input_counter { Some(s) => unsafe { emulator.read_mem(s, &mut buf); }, @@ -299,41 +274,12 @@ where { let emulator = hooks.emulator(); let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - trigger_collection(emulator, (None,Some(pc)), h); + let src = read_rec_return_stackframe(emulator, 0xfffffffc); + trigger_collection(emulator, (src, pc), CaptureEvent::ISRStart, h); + // println!("Exec ISR Call {:#x} {:#x} {}", src, pc, get_icount(emulator)); } -//============================= Trace syscall execution - -pub fn exec_syscall_hook( - hooks: &mut QemuHooks, - _state: Option<&mut S>, - pc: GuestAddr, -) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - let emulator = hooks.emulator(); - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - - let mut edge = (None, Some(pc)); - unsafe { - LAST_API_CALL.with(|x| { - match (*x.get()).take() { - Some(s) => { - edge.0=Some(s.0); - trigger_collection(emulator, edge, h); - }, - None => (), - } - }); - } - -} - -//============================= Trace jumps to syscalls - -thread_local!(static LAST_API_CALL : UnsafeCell> = UnsafeCell::new(None)); +//============================= Trace syscalls and returns pub fn gen_jmp_is_syscall( hooks: &mut QemuHooks, @@ -368,6 +314,45 @@ where return None; } +pub fn trace_jmp( + hooks: &mut QemuHooks, + _state: Option<&mut S>, + src: GuestAddr, mut dest: GuestAddr, id: u64 +) +where + S: UsesInput, + QT: QemuHelperTuple, +{ + let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); + let emulator = hooks.emulator(); + if id == 1 { // API call + trigger_collection(emulator, (src, dest), CaptureEvent::APIStart, h); + // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } else if id == 2 { // API return + // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. + if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() { + + let mut edge = (0, 0); + edge.0=in_any_range(&h.api_fn_ranges, src).unwrap().start; + edge.1=dest; + + trigger_collection(emulator, edge, CaptureEvent::APIEnd, h); + // println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } + } else if id == 3 { // ISR return + dest = read_rec_return_stackframe(emulator, dest); + + let mut edge = (0, 0); + edge.0=in_any_range(&h.isr_ranges, src).unwrap().start; + edge.1=dest; + + trigger_collection(emulator, edge, CaptureEvent::ISREnd, h); + // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); + } +} + +//============================= Utility functions + fn get_icount(emulator : &Emulator) -> u64 { unsafe { // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround @@ -380,43 +365,24 @@ fn get_icount(emulator : &Emulator) -> u64 { } } -pub fn trace_api_call( - hooks: &mut QemuHooks, - _state: Option<&mut S>, - src: GuestAddr, dest: GuestAddr, id: u64 -) -where - S: UsesInput, - QT: QemuHelperTuple, -{ - if id == 1 { // API call - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.emulator(); - trigger_collection(emulator, (Some(src), Some(dest)), h); - // println!("Exec API Call {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } else if id == 2 { // API return - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - // Ignore returns to other APIs or ISRs. We only account for the first call depth of API calls from user space. - if in_any_range(&h.api_fn_ranges, dest).is_none() && in_any_range(&h.isr_ranges, dest).is_none() { - let emulator = hooks.emulator(); - - let mut edge = (None, None); - edge.0=Some(in_any_range(&h.api_fn_ranges, src).unwrap().start); - edge.1=Some(dest); - - trigger_collection(emulator, edge, h); - // println!("Exec API Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } - } else if id == 3 { // ISR return - let h = hooks.helpers().match_first_type::().expect("QemuSystemHelper not found in helper tupel"); - let emulator = hooks.emulator(); - - let mut edge = (None, None); - edge.0=Some(in_any_range(&h.isr_ranges, src).unwrap().start); - - trigger_collection(emulator, edge, h); - // println!("Exec ISR Return Edge {:#x} {:#x} {}", src, dest, get_icount(emulator)); - } +fn read_rec_return_stackframe(emu : &Emulator, lr : GuestAddr) -> GuestAddr { + let lr_ = lr & u32::MAX-1; + if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF8 || lr_ == 0xFFFFFFF0 { + unsafe { + // if 0xFFFFFFF0/1 0xFFFFFFF8/9 -> "main stack" MSP + let mut buf = [0u8; 4]; + let sp : GuestAddr = if lr_ == 0xfffffffc || lr_ == 0xFFFFFFF0 { // PSP + read_user_reg_unchecked(emu) as u32 + } else { + emu.read_reg(13).unwrap() + }; + let ret_pc = sp+0x18; // https://developer.arm.com/documentation/dui0552/a/the-cortex-m3-processor/exception-model/exception-entry-and-return + emu.read_mem(ret_pc, buf.as_mut_slice()); + return u32::from_le_bytes(buf); + // elseif 0xfffffffc/d + }} else { + return lr; + }; } pub fn in_any_range<'a>(ranges: &'a Vec<(String, Range)>, addr : GuestAddr) -> Option<&'a std::ops::Range> { diff --git a/fuzzers/FRET/src/systemstate/mod.rs b/fuzzers/FRET/src/systemstate/mod.rs index 8ba7cc3b50..a18c14be22 100644 --- a/fuzzers/FRET/src/systemstate/mod.rs +++ b/fuzzers/FRET/src/systemstate/mod.rs @@ -48,7 +48,7 @@ pub struct RawFreeRTOSSystemState { delay_list_overflow: freertos::List_t, dumping_ground: HashMap, input_counter: u32, - edge: (Option,Option), + edge: (GuestAddr,GuestAddr), capture_point: (CaptureEvent,String) } /// List of system state dumps from QemuHelpers diff --git a/fuzzers/FRET/src/systemstate/observers.rs b/fuzzers/FRET/src/systemstate/observers.rs index 6275462d65..ab4c3cc933 100644 --- a/fuzzers/FRET/src/systemstate/observers.rs +++ b/fuzzers/FRET/src/systemstate/observers.rs @@ -34,6 +34,7 @@ pub struct QemuSystemStateObserver pub last_states: HashMap, pub last_trace: Vec, pub last_input: Vec, + pub success: bool, name: String, } @@ -59,6 +60,7 @@ where let mut temp = states2intervals(temp.0, temp.1); self.last_trace = temp.0; self.last_states = temp.1; + self.success = temp.2; // println!("{:?}",temp); } // let abbs = extract_abbs_from_trace(&self.last_run); @@ -88,7 +90,7 @@ impl HasLen for QemuSystemStateObserver impl QemuSystemStateObserver { pub fn new() -> Self { - Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new() } + Self{last_run: vec![], last_trace: vec![], last_input: vec![], name: "systemstate".to_string(), last_states: HashMap::new(), success: false } } } @@ -132,10 +134,11 @@ 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, Vec<(u64, CaptureEvent, String, (Option, Option))>) { +fn refine_system_states(input: &mut Vec) -> (Vec, Vec<(u64, CaptureEvent, String, (u32, u32))>) { let mut ret = (Vec::<_>::new(), Vec::<_>::new()); for mut i in input.drain(..) { let cur = RefinedTCB::from_tcb_owned(i.current_tcb); + // println!("Refine: {} {:?} {:?} {:x}-{:x}", cur.task_name, i.capture_point.0, i.capture_point.1.to_string(), i.edge.0, i.edge.1); // collect ready list let mut collector = Vec::::new(); for j in i.prio_ready_lists.into_iter().rev() { @@ -154,22 +157,21 @@ fn refine_system_states(input: &mut Vec) -> (Vec, meta: Vec<(u64, CaptureEvent, String, (Option, Option))>) -> (Vec, HashMap) { - if trace.len() == 0 {return (Vec::new(), HashMap::new());} +fn states2intervals(trace: Vec, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec, HashMap, bool) { + if trace.len() == 0 {return (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 let mut level_of_task : HashMap<&str, u8> = HashMap::new(); let mut ret: Vec = vec![]; - let mut edges: Vec<(Option, Option)> = vec![]; + let mut edges: Vec<(u32, u32)> = vec![]; let mut last_hash : u64 = trace[0].get_hash(); let mut table : HashMap = HashMap::new(); table.insert(last_hash, trace[0].clone()); @@ -184,6 +186,9 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt 0 }, CaptureEvent::APIStart => { // API start can only be called in the app + if !level_of_task.contains_key(curr_name) { // Should not happen, apps start from an ISR End. Some input exibited this behavior for unknown reasons + level_of_task.insert(curr_name, 0); + } *level_of_task.get_mut(curr_name).unwrap()=1; 1 }, @@ -239,111 +244,109 @@ fn states2intervals(trace: Vec, meta: Vec<(u64, Capt last_hash = next_hash; edges.push((meta[i].3.1, meta[i+1].3.0)); } - add_abb_info(&mut ret, &table, &edges); - (ret, table) + let t = add_abb_info(&mut ret, &table, &edges); + (ret, table, t) } -fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(Option, Option)>) { +fn add_abb_info(trace: &mut Vec, table: &HashMap, edges: &Vec<(u32, u32)>) -> bool { + let mut ret = true; 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(); + // let mut open_abb_at_this_task_or_level : HashMap<(u8,&str),usize> = HashMap::new(); + let mut open_abb_at_this_ret_addr_and_task : HashMap<(u32,&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 {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + // let open_abb = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level + let open_abb = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})).to_owned(); // apps/apis are differentiated by task name, isrs by nested level // println!("Edge {:x}-{:x}", edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff)); - 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 { - // assert_ne!(open_abb,None); - if let Some(last) = open_abb_at_this_task_or_level.get(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""})) { - wip_abb_trace.push(wip_abb_trace[*last].clone()); - } else { - // panic!(); - eprintln!("Continued block with no start {} {:?} {:?} {:x}-{:x} {} {}", trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff),task_has_started.contains(curr_name),trace[i].level); - panic!(); - wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1.unwrap_or(0), ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))) - } - } - }, - _ => panic!("Undefined block start") + match trace[i].start_capture.0 { + // generic api abb start + CaptureEvent::APIStart => { + // assert_eq!(open_abb, None); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, 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); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, 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); + ret &= open_abb.is_none(); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].0, 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); + ret &= open_abb.is_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_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), i); + task_has_started.insert(curr_name.clone()); + } else { + if let Some(last) = open_abb_at_this_ret_addr_and_task.get(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})) { + let last = last.clone(); // required to drop immutable reference + wip_abb_trace.push(wip_abb_trace[last].clone()); + // if the abb is interrupted again, it will need to continue at edge[i].1 + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].0, if trace[i].level<2 {&curr_name} else {""})); + open_abb_at_this_ret_addr_and_task.insert((edges[i].1, if trace[i].level<2 {&curr_name} else {""}), last); // order matters! + } else { + // panic!(); + // println!("Continued block with no start {} {} {:?} {:?} {:x}-{:x} {} {}", curr_name, trace[i].start_tick, trace[i].start_capture, trace[i].end_capture, edges[i].0, edges[i].1, task_has_started.contains(curr_name),trace[i].level); + // println!("{:x?}", open_abb_at_this_ret_addr_and_task); + ret = false; + wip_abb_trace.push(Rc::new(RefCell::new(AtomicBasicBlock{start: edges[i].1, ends: HashSet::new(), level: if trace[i].level<2 {trace[i].level} else {2}}))) + } } - 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") - } - } + }, + _ => panic!("Undefined block start") } - // println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0.unwrap_or(0xffff), edges[i].1.unwrap_or(0xffff), ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); + 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); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, 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); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, 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); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + // end anything + CaptureEvent::End => { + RefCell::borrow_mut(&*wip_abb_trace[i]).ends.insert(edges[i].1); + open_abb_at_this_ret_addr_and_task.remove(&(edges[i].1, if trace[i].level<2 {&curr_name} else {""})); + }, + CaptureEvent::ISRStart => (), + _ => panic!("Undefined block end") + } + // println!("{} {} {:x}-{:x} {:x}-{:x} {:?} {:?} {}",curr_name, trace[i].level, edges[i].0, edges[i].1, ((*wip_abb_trace[i])).borrow().start, ((*wip_abb_trace[i])).borrow().ends.iter().next().unwrap_or(&0xffff), trace[i].start_capture, trace[i].end_capture, trace[i].start_tick); + // println!("{:x?}", open_abb_at_this_ret_addr_and_task); } - drop(open_abb_at_this_task_or_level); + // drop(open_abb_at_this_task_or_level); for i in 0..trace.len() { trace[i].abb = Some((*wip_abb_trace[i]).borrow().clone()); } + return ret; }