two-way isr edges, graceful parsing error handling
This commit is contained in:
parent
69d0c6f9bb
commit
1146c2c1e5
@ -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(|| {
|
||||
|
@ -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);
|
||||
|
@ -225,3 +225,59 @@ impl DumpSystraceFeedback
|
||||
Self {dumpfile: None, dump_metadata: true, last_trace: None, last_states: None}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SystraceErrorFeedback
|
||||
{
|
||||
dump_case: bool
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for SystraceErrorFeedback
|
||||
where
|
||||
S: State + UsesInput + MaybeHasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>
|
||||
{
|
||||
let observer = observers.match_name::<QemuSystemStateObserver>("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<OT>(&mut self, _state: &mut S, _observers: &OT, _testcase: &mut Testcase<S::Input>) -> 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}
|
||||
}
|
||||
}
|
@ -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::<QT, S>), false);
|
||||
}
|
||||
_hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_api_call::<QT, S>));
|
||||
_hooks.jmps(Hook::Function(gen_jmp_is_syscall::<QT, S>), Hook::Function(trace_jmp::<QT, S>));
|
||||
}
|
||||
|
||||
// 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<OT>(&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::<i32, u32>(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<GuestAddr>,Option<GuestAddr>), h: &QemuSystemStateHelper) {
|
||||
fn trigger_collection(emulator: &Emulator, edge: (GuestAddr, GuestAddr), event: CaptureEvent, h: &QemuSystemStateHelper) {
|
||||
let listbytes : GuestAddr = GuestAddr::try_from(std::mem::size_of::<freertos::List_t>()).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());
|
||||
}
|
||||
}
|
||||
},
|
||||
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) {
|
||||
match event {
|
||||
CaptureEvent::APIStart => {
|
||||
let s = h.api_fn_addrs.get(&edge.1).unwrap();
|
||||
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::<QemuSystemStateHelper>().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<QT, S>(
|
||||
hooks: &mut QemuHooks<QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
pc: GuestAddr,
|
||||
)
|
||||
where
|
||||
S: UsesInput,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
let emulator = hooks.emulator();
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().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<Option<(GuestAddr,GuestAddr)>> = UnsafeCell::new(None));
|
||||
//============================= Trace syscalls and returns
|
||||
|
||||
pub fn gen_jmp_is_syscall<QT, S>(
|
||||
hooks: &mut QemuHooks<QT, S>,
|
||||
@ -368,6 +314,45 @@ where
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn trace_jmp<QT, S>(
|
||||
hooks: &mut QemuHooks<QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
src: GuestAddr, mut dest: GuestAddr, id: u64
|
||||
)
|
||||
where
|
||||
S: UsesInput,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().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<QT, S>(
|
||||
hooks: &mut QemuHooks<QT, S>,
|
||||
_state: Option<&mut S>,
|
||||
src: GuestAddr, dest: GuestAddr, id: u64
|
||||
)
|
||||
where
|
||||
S: UsesInput,
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
if id == 1 { // API call
|
||||
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().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::<QemuSystemStateHelper>().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::<QemuSystemStateHelper>().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<u32>)>, addr : GuestAddr) -> Option<&'a std::ops::Range<GuestAddr>> {
|
||||
|
@ -48,7 +48,7 @@ pub struct RawFreeRTOSSystemState {
|
||||
delay_list_overflow: freertos::List_t,
|
||||
dumping_ground: HashMap<u32,freertos::rtos_struct>,
|
||||
input_counter: u32,
|
||||
edge: (Option<GuestAddr>,Option<GuestAddr>),
|
||||
edge: (GuestAddr,GuestAddr),
|
||||
capture_point: (CaptureEvent,String)
|
||||
}
|
||||
/// List of system state dumps from QemuHelpers
|
||||
|
@ -34,6 +34,7 @@ pub struct QemuSystemStateObserver
|
||||
pub last_states: HashMap<u64, ReducedFreeRTOSSystemState>,
|
||||
pub last_trace: Vec<ExecInterval>,
|
||||
pub last_input: Vec<u8>,
|
||||
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<u32,rtos_struct>) ->
|
||||
ret
|
||||
}
|
||||
/// Drains a List of raw SystemStates to produce a refined trace
|
||||
fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) {
|
||||
fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<ReducedFreeRTOSSystemState>, 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::<RefinedTCB>::new();
|
||||
for j in i.prio_ready_lists.into_iter().rev() {
|
||||
@ -154,22 +157,21 @@ fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> (Vec<Reduced
|
||||
delay_list_after: delay_list,
|
||||
// input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
|
||||
});
|
||||
// println!("Refine: {:?} {:?} {:x}-{:x}",i.capture_point.0, i.capture_point.1.to_string(), i.edge.0.unwrap_or(0), i.edge.1.unwrap_or(0));
|
||||
ret.1.push((i.qemu_tick, i.capture_point.0, i.capture_point.1.to_string(), i.edge));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Transform the states and metadata into a list of ExecIntervals
|
||||
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (Option<u32>, Option<u32>))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>) {
|
||||
if trace.len() == 0 {return (Vec::new(), HashMap::new());}
|
||||
fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, meta: Vec<(u64, CaptureEvent, String, (u32, u32))>) -> (Vec<ExecInterval>, HashMap<u64, ReducedFreeRTOSSystemState>, bool) {
|
||||
if trace.len() == 0 {return (Vec::new(), HashMap::new(), true);}
|
||||
let mut isr_stack : VecDeque<u8> = 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<ExecInterval> = vec![];
|
||||
let mut edges: Vec<(Option<u32>, Option<u32>)> = vec![];
|
||||
let mut edges: Vec<(u32, u32)> = vec![];
|
||||
let mut last_hash : u64 = trace[0].get_hash();
|
||||
let mut table : HashMap<u64, ReducedFreeRTOSSystemState> = HashMap::new();
|
||||
table.insert(last_hash, trace[0].clone());
|
||||
@ -184,6 +186,9 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, 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,72 +244,70 @@ fn states2intervals(trace: Vec<ReducedFreeRTOSSystemState>, 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<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(Option<u32>, Option<u32>)>) {
|
||||
fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeRTOSSystemState>, edges: &Vec<(u32, u32)>) -> bool {
|
||||
let mut ret = true;
|
||||
let mut task_has_started : HashSet<String> = HashSet::new();
|
||||
let mut wip_abb_trace : Vec<Rc<RefCell<AtomicBasicBlock>>> = 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}})));
|
||||
// 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);
|
||||
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}})));
|
||||
// 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);
|
||||
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}})));
|
||||
// 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);
|
||||
// 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_task_or_level.insert( (trace[i].level, if trace[i].level<2 {&curr_name} else {""}) , i);
|
||||
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 {
|
||||
// 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());
|
||||
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!();
|
||||
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}})))
|
||||
// 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}})))
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -314,36 +317,36 @@ fn add_abb_info(trace: &mut Vec<ExecInterval>, table: &HashMap<u64, ReducedFreeR
|
||||
// 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 {""}));
|
||||
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.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
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.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
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.unwrap());
|
||||
open_abb_at_this_task_or_level.remove(&(trace[i].level, if trace[i].level<2 {&curr_name} else {""}));
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user