implement STGSnippetStage, fix missing metadata

This commit is contained in:
Alwin Berger 2024-10-29 14:07:52 +01:00
parent 3d0c0247b7
commit 013f3db487
9 changed files with 185 additions and 62 deletions

View File

@ -70,3 +70,4 @@ clap = { version = "4.4.11", features = ["derive"] }
csv = "1.3.0"
log = "0.4"
simple_moving_average = "1.0.2"
itertools = "0.13.0"

View File

@ -15,7 +15,7 @@ edges::{self, edges_map_mut_ptr, QemuEdgeCoverageHelper, MAX_EDGES_FOUND}, elf::
};
use rand::{SeedableRng, StdRng, Rng};
use crate::{
systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{
systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, load_symbol, try_load_symbol, QemuSystemStateHelper}, mutational::{input_bytes_to_interrupt_times, InterruptShiftStage, STGSnippetStage}, observers::QemuSystemStateObserver, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}}, time::{
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}
}
};
@ -478,7 +478,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
#[cfg(feature = "sched_afl",)]
let scheduler = TimeMaximizerCorpusScheduler::new(&edges_observer,TimeProbMassScheduler::new());
#[cfg(feature = "sched_stg")]
let scheduler = GraphMaximizerCorpusScheduler::new(&stg_coverage_observer,TimeProbMassScheduler::new());
let scheduler = GraphMaximizerCorpusScheduler::non_metadata_removing(&stg_coverage_observer,TimeProbMassScheduler::new());
#[cfg(feature = "sched_genetic")]
let scheduler = GenerationScheduler::new();
@ -520,7 +520,8 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
let mutator = StdScheduledMutator::new(mutations);
let stages = (systemstate::report::SchedulerStatsStage::default(),());
let mut stages = (StdMutationalStage::new(mutator), stages);
let stages = (StdMutationalStage::new(mutator), stages);
let mut stages = (STGSnippetStage::new(input_addr), stages);
#[cfg(feature = "fuzz_int")]
let mut stages = (InterruptShiftStage::new(&interrupt_config), stages);

View File

@ -192,9 +192,9 @@ where
let names : Vec<String> = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect();
match &self.dumpfile {
Some(s) => {
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))) {
let per_task_metadata = if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(&x.name) == observer.select_task.as_ref()).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) {
// 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();
let t : Vec<_> = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect();
// task_name -> addr -> (count, time)
let mut ret : HashMap<String, HashMap<u32, (usize, usize, u64)>> = HashMap::new();
let mut t2 = t.clone();
@ -212,7 +212,7 @@ where
}
});
});
dbg!(&ret);
// 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");

View File

@ -8,6 +8,7 @@ use std::hash::Hasher;
use std::hash::Hash;
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use itertools::Itertools;
use freertos::TCB_t;
@ -452,18 +453,34 @@ impl TaskJob {
self.hash_cache
}
}
pub fn try_update(&mut self, other: &mut JobInstance) -> bool {
assert_eq!(self.get_hash(), other.get_hash());
pub fn try_update(&mut self, other: &JobInstance) -> bool {
assert_eq!(self.get_hash(), other.get_hash_cached());
let mut ret = false;
if other.exec_ticks > self.woet_ticks {
self.woet_ticks = other.exec_ticks;
self.woet_per_abb = other.ticks_per_abb.clone();
other.mem_reads.sort_by(|a,b| a.0.cmp(&b.0));
self.worst_bytes = other.mem_reads.iter().map(|x| x.1).collect();
self.worst_bytes = other.mem_reads.iter().sorted_by(|a,b| a.0.cmp(&b.0)).map(|x| x.1).collect();
ret = true;
}
ret
}
pub fn from_instance(input: &JobInstance) -> Self {
let c = input.get_hash_cached();
Self {
name: input.name.clone(),
worst_bytes: input.mem_reads.iter().map(|x| x.1.clone()).collect(),
woet_ticks: input.exec_ticks,
woet_per_abb: input.ticks_per_abb.clone(),
abbs: input.abbs.clone(),
hash_cache: c
}
}
pub fn map_bytes_onto(&self, input: &JobInstance, offset: Option<u32>) -> Vec<(u32,u8)> {
if input.mem_reads.len() == 0 {return vec![];}
let ret = input.mem_reads.iter().take(self.worst_bytes.len()).enumerate().filter_map(|(idx,(addr,oldbyte))| if self.worst_bytes[idx]!=*oldbyte {Some((*addr-offset.unwrap_or_default(), self.worst_bytes[idx]))} else {None}).collect();
// eprintln!("Mapped: {:?}", ret);
ret
}
}

View File

@ -9,7 +9,7 @@ use libafl_bolts::{rands::{
random_seed, Rand, StdRand
}, Named};
use libafl::{
common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error
common::{HasMetadata, HasNamedMetadata}, corpus::{self, Corpus}, events::{Event, EventFirer, EventProcessor, LogSeverity}, fuzzer::Evaluator, inputs::{HasMutatorBytes, HasTargetBytes, Input, MultipartInput}, mark_feature_time, prelude::{new_hash_feedback, AggregatorOps, CorpusId, MutationResult, Mutator, UserStats, UserStatsValue, UsesInput}, stages::Stage, start_timer, state::{HasCorpus, HasRand, MaybeHasClientPerfMonitor, UsesState}, Error
};
use libafl::prelude::State;
use petgraph::{graph::NodeIndex, graph::{self, DiGraph}};
@ -19,7 +19,7 @@ use std::borrow::Cow;
use simple_moving_average::SMA;
use super::stg::{STGEdge, STGNode};
use super::{stg::{STGEdge, STGNode}, JobInstance};
// pub static mut MINIMUM_INTER_ARRIVAL_TIME : u32 = 1000 /*us*/ * QEMU_ISNS_PER_USEC;
// one isn per 2**4 ns
@ -88,7 +88,7 @@ pub fn try_force_new_branches(interrupt_ticks : &[u32], fbs: &STGFeedbackState,
for (num,&interrupt_time) in interrupt_ticks.iter().enumerate() {
let lower_bound = if num==0 {FIRST_INT} else {interrupt_ticks[num-1].saturating_add(config.1 * QEMU_ISNS_PER_USEC)};
let next = if interrupt_ticks.len()>num+1 {interrupt_ticks[num+1]} else {u32::MAX};
for exec_interval in meta.intervals.iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) {
for exec_interval in meta.intervals().iter().filter(|x| x.start_tick >= lower_bound as u64 && x.start_tick < next as u64) {
if !(exec_interval.start_capture.0==CaptureEvent::ISRStart) { // shortcut to skip interrupt handers without node lookup
let node_index = fbs.state_abb_hash_index.get(&exec_interval.get_hash_index()).unwrap();
if !has_interrupt_handler_non_systick(&fbs.graph, node_index.clone()) {
@ -306,8 +306,8 @@ where
// calculate hits and identify snippets
let mut last_m = false;
let mut marks : Vec<(&ExecInterval, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
for i in 0..trace.intervals.len() {
let curr = &trace.intervals[i];
for i in 0..trace.intervals().len() {
let curr = &trace.intervals()[i];
let m = old_interrupt_times.iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64)));
if m {
marks.push((curr, i, 1));
@ -323,7 +323,7 @@ where
for i in 0..old_interrupt_times.len() {
// bounds based on minimum inter-arrival time
let mut lb = FIRST_INT;
let mut ub : u32 = trace.intervals[trace.intervals.len()-1].end_tick.try_into().expect("ticks > u32");
let mut ub : u32 = trace.intervals()[trace.intervals().len()-1].end_tick.try_into().expect("ticks > u32");
if i > 0 {
// use the new times, because changes to preceding timings are not accounted for yet
lb = u32::saturating_add(new_interrupt_times[i-1], interrup_config.1 * QEMU_ISNS_PER_USEC);
@ -453,18 +453,23 @@ where
pub fn try_worst_snippets(bytes : &[u8], fbs: &STGFeedbackState, meta: &STGNodeMetadata) -> Option<Vec<u8>> {
let mut new = false;
let mut ret = Vec::new();
for (num,interval) in meta.intervals.iter().enumerate() {
for (num,interval) in meta.intervals().iter().enumerate() {
todo!();
}
if new {Some(ret)} else {None}
}
static mut num_snippet_stage_execs : u64 = 0;
static mut num_snippet_rerun : u64 = 0;
static mut num_snippet_success : u64 = 0;
/// The default mutational stage
#[derive(Clone, Debug, Default)]
pub struct STGSnippetStage<E, EM, Z> {
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>,
input_addr: u32
}
impl<E, EM, Z> STGSnippetStage<E, EM, Z>
@ -474,8 +479,36 @@ where
Z: Evaluator<E, EM>,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand,
{
pub fn new() -> Self {
Self { phantom: PhantomData }
pub fn new(input_addr: u32) -> Self {
Self { phantom: PhantomData, input_addr }
}
}
impl<E, EM, Z, I> STGSnippetStage<E, EM, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
EM: EventFirer,
Z: Evaluator<E, EM>,
Z::State: MaybeHasClientPerfMonitor + HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
<Z::State as UsesInput>::Input: Input,
Z::State: UsesInput<Input = MultipartInput<I>>,
I: HasMutatorBytes + Default
{
fn report_stats(&self, state: &mut <STGSnippetStage<E, EM, Z> as UsesState>::State, manager: &mut EM) {
unsafe {
let _ = manager.fire(
state,
Event::UpdateUserStats {
name: Cow::from("STGSnippetStage"),
value: UserStats::new(
UserStatsValue::String(Cow::from(format!("{} -> {}/{} {:.1}% ", num_snippet_stage_execs, num_snippet_success, num_snippet_rerun, num_snippet_success as f32 * 100.0 / num_snippet_rerun as f32))),
AggregatorOps::None,
),
phantom: PhantomData,
},
);
}
}
}
@ -500,11 +533,14 @@ where
let mut myrand = StdRand::new();
myrand.set_seed(state.rand_mut().next());
let mut do_rerun = false;
let current_case = state.current_testcase()?;
let old_input = current_case.input().as_ref().unwrap();
let mut new_input = old_input.clone();
if let Some(bytes) = old_input.parts_by_name("bytes").next() {
let new_bytes = new_input.parts_by_name("bytes").next();
let new_bytes = new_input.parts_by_name_mut("bytes").next().expect("bytes not found in multipart input").1.bytes_mut();
// dbg!(current_case.metadata_map());
// eprintln!("Run mutator {}", current_case.metadata_map().get::<STGNodeMetadata>().is_some());
if let Some(meta) = current_case.metadata_map().get::<STGNodeMetadata>() {
let feedbackstate = match state
.named_metadata_map()
@ -514,10 +550,29 @@ where
panic!("STGfeedbackstate not visible")
}
};
todo!();
// Maximize all snippets
// dbg!(meta.jobs().len());
for jobinst in meta.jobs().iter() {
match feedbackstate.worst_task_jobs.get(&jobinst.get_hash_cached()) {
Some(worst) => {
let new = worst.map_bytes_onto(jobinst, Some(self.input_addr));
do_rerun |= new.len() > 0;
for (addr, byte) in new {
new_bytes[addr as usize] = byte;
}
},
Option::None => {}
}
}
}
drop(current_case);
unsafe {num_snippet_stage_execs+=1;}
if do_rerun {
unsafe {num_snippet_rerun+=1;}
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, new_input)?;
if corpus_idx.is_some() { unsafe{num_snippet_success+=1; self.report_stats(state, manager);}}
}
} else if {unsafe{num_snippet_stage_execs}} % 5 == 0 {self.report_stats(state, manager);}
Ok(())
}

View File

@ -41,9 +41,9 @@ pub struct QemuSystemStateObserver<I>
pub last_trace: Vec<ExecInterval>,
pub last_reads: Vec<Vec<(u32, u8)>>,
pub last_input: I,
pub job_instances: Vec<(u64, u64, String)>,
pub job_instances: Vec<JobInstance>,
pub do_report: bool,
pub worst_job_instances: HashMap<String, u64>,
pub worst_job_instances: HashMap<String, JobInstance>,
pub select_task: Option<String>,
pub success: bool,
name: Cow<'static, str>,
@ -96,18 +96,18 @@ where
hash_cache: 0
}
}).collect::<Vec<_>>();
println!("Instances: {:?}",&job_instances);
// println!("Instances: {:?}",&job_instances);
self.job_instances = job_instances;
let observer = &self;
let mut worst_case_per_task = HashMap::new();
let mut worst_case_per_task : HashMap<String, JobInstance> = HashMap::new();
observer.job_instances.iter().for_each(|x| {
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 {
*old=time;
if worst_case_per_task.get(&x.name).is_some() {
let old = worst_case_per_task.get_mut(&x.name).unwrap();
if x.exec_ticks > old.exec_ticks {
old.exec_ticks=x.exec_ticks;
}
} else {
worst_case_per_task.insert(x.2.clone(), time);
worst_case_per_task.insert(x.name.clone(), x.clone());
}
});
self.worst_job_instances = worst_case_per_task;
@ -160,7 +160,7 @@ where I: Default {
Self{last_run: vec![], last_trace: vec![], last_reads: vec![], last_input: I::default(), worst_job_instances: HashMap::new(), do_report: false, select_task: select_task.clone(), name: Cow::from("systemstate".to_string()), last_states: HashMap::new(), success: false, job_instances: vec![]}
}
pub fn last_runtime(&self) -> u64 {
self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).unwrap_or(&0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()})
self.select_task.as_ref().map(|x| self.worst_job_instances.get(x).map(|y| y.response-y.release).unwrap_or(0).clone()).unwrap_or(unsafe{libafl_qemu::sys::icount_get_raw()})
}
}
impl<I> Default for QemuSystemStateObserver<I>

View File

@ -101,7 +101,7 @@ where
.get(idx)?
.borrow()
.metadata_map()
.get::<STGNodeMetadata>().map_or(0, |x| x.nodes.len());
.get::<STGNodeMetadata>().map_or(0, |x| x.nodes().len());
let m = self.get_update_trace_length(state,l);
state.rand_mut().below(m as usize) > l
} && state.rand_mut().coinflip(self.skip_non_favored_prob)

View File

@ -29,8 +29,10 @@ use serde::{Deserialize, Serialize};
use super::AtomicBasicBlock;
use super::CaptureEvent;
use super::ExecInterval;
use super::JobInstance;
use super::ReducedFreeRTOSSystemState;
use super::observers::QemuSystemStateObserver;
use super::TaskJob;
use petgraph::prelude::DiGraph;
use petgraph::graph::NodeIndex;
use petgraph::Direction;
@ -151,7 +153,9 @@ pub struct STGFeedbackState
worst_observed_per_aggegated_path: HashMap<Vec<AtomicBasicBlock>,u64>,
worst_observed_per_abb_path: HashMap<u64,u64>,
worst_observed_per_stg_path: HashMap<u64,u64>,
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>
worst_abb_exec_count: HashMap<AtomicBasicBlock, usize>,
// Metadata about job instances
pub worst_task_jobs: HashMap<u64, TaskJob>,
}
impl Default for STGFeedbackState {
@ -185,7 +189,8 @@ impl Default for STGFeedbackState {
worst_observed_per_stg_path: HashMap::new(),
worst_abb_exec_count: HashMap::new(),
systemstate_index,
state_abb_hash_index
state_abb_hash_index,
worst_task_jobs: HashMap::new(),
}
}
}
@ -201,17 +206,18 @@ impl Named for STGFeedbackState
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct STGNodeMetadata {
pub nodes: Vec<NodeIndex>,
pub edges: Vec<EdgeIndex>,
pub abbs: u64,
pub aggregate: u64,
pub top_abb_counts: Vec<u64>,
pub intervals: Vec<ExecInterval>,
nodes: Vec<NodeIndex>,
edges: Vec<EdgeIndex>,
abbs: u64,
aggregate: u64,
top_abb_counts: Vec<u64>,
intervals: Vec<ExecInterval>,
jobs: Vec<JobInstance>,
indices: Vec<usize>,
tcref: isize,
}
impl STGNodeMetadata {
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abbs: u64, aggregate: u64, top_abb_counts: Vec<u64>, intervals: Vec<ExecInterval>) -> Self{
pub fn new(nodes: Vec<NodeIndex>, edges: Vec<EdgeIndex>, abbs: u64, aggregate: u64, top_abb_counts: Vec<u64>, intervals: Vec<ExecInterval>, jobs: Vec<JobInstance>) -> Self {
#[allow(unused)]
let mut indices : Vec<_> = vec![];
#[cfg(all(feature = "sched_stg",not(any(feature = "sched_stg_pathhash",feature = "sched_stg_abbhash",feature = "sched_stg_aggregatehash"))))]
@ -233,7 +239,35 @@ impl STGNodeMetadata {
// indices.push(aggregate as usize);
indices = top_abb_counts.iter().map(|x| (*x) as usize).collect();
}
Self {indices, intervals, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0}
Self {indices, intervals, jobs, nodes, abbs, aggregate, top_abb_counts, edges, tcref: 0}
}
pub fn nodes(&self) -> &Vec<NodeIndex> {
&self.nodes
}
pub fn edges(&self) -> &Vec<EdgeIndex> {
&self.edges
}
pub fn abbs(&self) -> u64 {
self.abbs
}
pub fn aggregate(&self) -> u64 {
self.aggregate
}
pub fn top_abb_counts(&self) -> &Vec<u64> {
&self.top_abb_counts
}
pub fn intervals(&self) -> &Vec<ExecInterval> {
&self.intervals
}
pub fn jobs(&self) -> &Vec<JobInstance> {
&self.jobs
}
}
@ -317,6 +351,7 @@ pub struct StgFeedback
last_abbs_hash: Option<u64>, // only set, if it was interesting
last_aggregate_hash: Option<u64>, // only set, if it was interesting
last_top_abb_hashes: Option<Vec<u64>>, // only set, if it was interesting
last_job_trace: Option<Vec<JobInstance>>, // only set, if it was interesting
dump_path: Option<PathBuf>
}
#[cfg(feature = "feed_stg")]
@ -340,6 +375,9 @@ const INTEREST_PATH : bool = false;
const INTEREST_ABBPATH : bool = false;
#[cfg(not(feature = "feed_stg_aggregatehash"))]
const INTEREST_AGGREGATE : bool = false;
const INTEREST_JOB_INSTANCE : bool = true;
fn set_observer_map(trace : &Vec<EdgeIndex>) {
unsafe {
for i in 0..MAX_STG_NUM {
@ -526,8 +564,23 @@ where
}
};
// --------------------------------- Update STG
let (nodetrace, edgetrace, mut interesting, mut updated) = StgFeedback::update_stg_interval(&observer.last_trace, &observer.last_reads, &observer.last_states, feedbackstate);
// --------------------------------- Update job instances
for i in observer.worst_job_instances.iter() {
interesting |= INTEREST_JOB_INSTANCE && if let Some(x) = feedbackstate.worst_task_jobs.get_mut(&i.1.get_hash_cached()) {
// eprintln!("Job instance already present");
x.try_update(i.1)
} else {
// eprintln!("New Job instance");
feedbackstate.worst_task_jobs.insert(i.1.get_hash_cached(), TaskJob::from_instance(&i.1));
true
}
};
self.last_job_trace = Some(observer.job_instances.clone());
// dbg!(&observer.job_instances);
{
let h = get_generic_hash(&edgetrace);
if let Some(x) = feedbackstate.worst_observed_per_stg_path.get_mut(&h) {
@ -547,8 +600,8 @@ where
let tmp = StgFeedback::abbs_in_exec_order(&observer.last_trace);
#[cfg(feature = "trace_job_response_times")]
let tmp = {
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))) {
let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.1 && x.end_tick > worst_instance.0 ).cloned().collect();
if let Some(worst_instance) = observer.job_instances.iter().filter(|x| Some(x.name.clone()) == observer.select_task).max_by(|a,b| (a.response-a.release).cmp(&(b.response-b.release))) {
let t = observer.last_trace.iter().filter(|x| x.start_tick < worst_instance.response && x.end_tick > worst_instance.release ).cloned().collect();
StgFeedback::abbs_in_exec_order(&t)
} else {
if observer.select_task.is_none() { // if nothing was selected, just take the whole trace, otherwise there is nothing interesting here
@ -633,12 +686,8 @@ where
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
fn append_metadata<EM, OT>(&mut self, _state: &mut S, _manager: &mut EM, _observers: &OT, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
let nodes = self.last_node_trace.take();
let edges = self.last_edge_trace.take();
match nodes {
Some(s) => testcase.metadata_map_mut().insert(STGNodeMetadata::new(s, edges.unwrap(), self.last_abbs_hash.take().unwrap_or_default(), self.last_aggregate_hash.take().unwrap_or_default(), self.last_top_abb_hashes.take().unwrap_or_default(), self.last_intervals.take().unwrap())),
Option::None => (),
}
let meta = STGNodeMetadata::new(self.last_node_trace.take().unwrap_or_default(), self.last_edge_trace.take().unwrap_or_default(), self.last_abbs_hash.take().unwrap_or_default(), self.last_aggregate_hash.take().unwrap_or_default(), self.last_top_abb_hashes.take().unwrap_or_default(), self.last_intervals.take().unwrap_or_default(), self.last_job_trace.take().unwrap_or_default());
testcase.metadata_map_mut().insert(meta);
Ok(())
}

View File

@ -342,7 +342,7 @@ where
.map
.insert(elem, idx);
}
println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::<TopRatedsMetadata>().unwrap().get_number());
// println!("Number of interesting corpus elements: {}", state.metadata_map_mut().get::<TopRatedsMetadata>().unwrap().get_number());
Ok(())
}