add some basic fuzzer
This commit is contained in:
parent
c1db0752c1
commit
ac181eb99d
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = []
|
std = []
|
||||||
|
showmap = []
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
417
fuzzers/wcet_qemu_sys/src/fuzzer.rs
Normal file
417
fuzzers/wcet_qemu_sys/src/fuzzer.rs
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
//! A singlethreaded QEMU fuzzer that can auto-restart.
|
||||||
|
|
||||||
|
use libafl::stats::SimpleStats;
|
||||||
|
use libafl::events::SimpleEventManager;
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use core::{cell::RefCell, time::Duration};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use nix::{self, unistd::dup};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::{self, File, OpenOptions},
|
||||||
|
io::{self, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
current_nanos, current_time,
|
||||||
|
os::dup2,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::{tuple_list, Merge},
|
||||||
|
},
|
||||||
|
corpus::{
|
||||||
|
Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, PowerQueueCorpusScheduler,
|
||||||
|
},
|
||||||
|
events::SimpleRestartingEventManager,
|
||||||
|
executors::{ExitKind, ShadowExecutor, TimeoutExecutor},
|
||||||
|
feedback_or,
|
||||||
|
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::SimpleMonitor,
|
||||||
|
mutators::{
|
||||||
|
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
||||||
|
StdMOptMutator, StdScheduledMutator, Tokens,
|
||||||
|
},
|
||||||
|
observers::{TimeObserver, VariableMapObserver},
|
||||||
|
stages::{
|
||||||
|
calibrate::CalibrationStage,
|
||||||
|
power::{PowerMutationalStage, PowerSchedule},
|
||||||
|
ShadowTracingStage, StdMutationalStage,
|
||||||
|
},
|
||||||
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
//asan::QemuAsanHelper,
|
||||||
|
cmplog,
|
||||||
|
cmplog::{CmpLogObserver, QemuCmpLogHelper},
|
||||||
|
edges,
|
||||||
|
edges::QemuEdgeCoverageHelper,
|
||||||
|
elf::EasyElf,
|
||||||
|
emu::Emulator,
|
||||||
|
filter_qemu_args,
|
||||||
|
snapshot_sys::QemuSysSnapshotHelper,
|
||||||
|
MmapPerms,
|
||||||
|
QemuExecutor,
|
||||||
|
Regs,
|
||||||
|
};
|
||||||
|
use crate::worst::HitcountsMapObserver;
|
||||||
|
use crate::worst::MapHitIncreaseFeedback;
|
||||||
|
|
||||||
|
|
||||||
|
/// The fuzzer main
|
||||||
|
pub fn main() {
|
||||||
|
// Registry the metadata types used in this fuzzer
|
||||||
|
// Needed only on no_std
|
||||||
|
//RegistryBuilder::register::<Tokens>();
|
||||||
|
|
||||||
|
let res = match App::new("wcet_qemu_fuzzer")
|
||||||
|
.version("0.4.0")
|
||||||
|
.author("Alwin Berger")
|
||||||
|
.about("LibAFL-based fuzzer for WCET in System Kernels.")
|
||||||
|
.arg(
|
||||||
|
Arg::new("k")
|
||||||
|
.long("libafl-kernel")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("out")
|
||||||
|
.help("The directory to place finds in ('corpus')")
|
||||||
|
.long("libafl-out")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("in")
|
||||||
|
.help("The directory to read initial inputs from ('seeds')")
|
||||||
|
.long("libafl-in")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("tokens")
|
||||||
|
.long("libafl-tokens")
|
||||||
|
.help("A file to read tokens from, to be used during fuzzing")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("logfile")
|
||||||
|
.long("libafl-logfile")
|
||||||
|
.help("Duplicates all output to this file")
|
||||||
|
.default_value("libafl.log"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("timeout")
|
||||||
|
.long("libafl-timeout")
|
||||||
|
.help("Timeout for each individual execution, in milliseconds")
|
||||||
|
.default_value("1000"),
|
||||||
|
)
|
||||||
|
.try_get_matches_from(filter_qemu_args())
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"Syntax: {}, --libafl-in <input> --libafl-out <output>\n{:?}",
|
||||||
|
env::current_exe()
|
||||||
|
.unwrap_or_else(|_| "fuzzer".into())
|
||||||
|
.to_string_lossy(),
|
||||||
|
err.info,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Workdir: {:?}",
|
||||||
|
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
|
||||||
|
let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string());
|
||||||
|
if fs::create_dir(&out_dir).is_err() {
|
||||||
|
println!("Out dir at {:?} already exists.", &out_dir);
|
||||||
|
if !out_dir.is_dir() {
|
||||||
|
println!("Out dir at {:?} is not a valid directory!", &out_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut crashes = out_dir.clone();
|
||||||
|
crashes.push("crashes");
|
||||||
|
out_dir.push("queue");
|
||||||
|
|
||||||
|
let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string());
|
||||||
|
if !in_dir.is_dir() {
|
||||||
|
println!("In dir at {:?} is not a valid directory!", &in_dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokens = res.value_of("tokens").map(PathBuf::from);
|
||||||
|
|
||||||
|
let logfile = PathBuf::from(res.value_of("logfile").unwrap().to_string());
|
||||||
|
|
||||||
|
let timeout = Duration::from_millis(
|
||||||
|
res.value_of("timeout")
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.parse()
|
||||||
|
.expect("Could not parse timeout in milliseconds"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let kernel = PathBuf::from(res.value_of("k").unwrap().to_string());
|
||||||
|
|
||||||
|
fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel)
|
||||||
|
.expect("An error occurred while fuzzing");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 {
|
||||||
|
let ret;
|
||||||
|
for i in &tab.program_headers {
|
||||||
|
if i.vm_range().contains(&vaddr.try_into().expect("Can not cast u64 to usize")) {
|
||||||
|
ret = vaddr-i.p_vaddr+i.p_paddr;
|
||||||
|
return ret - (ret % 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = vaddr;
|
||||||
|
// unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm
|
||||||
|
return ret - (ret % 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The actual fuzzer
|
||||||
|
fn fuzz(
|
||||||
|
corpus_dir: PathBuf,
|
||||||
|
objective_dir: PathBuf,
|
||||||
|
seed_dir: PathBuf,
|
||||||
|
tokenfile: Option<PathBuf>,
|
||||||
|
logfile: PathBuf,
|
||||||
|
timeout: Duration,
|
||||||
|
kernel: PathBuf,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
|
//=========== Initialize the Emulator
|
||||||
|
let mut args: Vec<String> = vec![
|
||||||
|
"qemu-system-arm",
|
||||||
|
"-machine","mps2-an385",
|
||||||
|
"-monitor", "null",
|
||||||
|
"-semihosting",
|
||||||
|
"--semihosting-config", "enable=on,target=native",
|
||||||
|
"-kernel", kernel.to_str().unwrap(),
|
||||||
|
"-serial", "stdio", "-nographic",
|
||||||
|
"-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2",
|
||||||
|
"-S"
|
||||||
|
].iter().map(|x| x.to_string()).collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
|
//=========== Analyze the binary to find the target function address
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let bin_path=kernel;
|
||||||
|
let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?;
|
||||||
|
|
||||||
|
let test_one_input_ptr = elf
|
||||||
|
.resolve_symbol("FUZZ_INPUT", 0)
|
||||||
|
.expect("Symbol FUZZ_INPUT not found");
|
||||||
|
let test_one_input_ptr = virt2phys(test_one_input_ptr,&elf.goblin());
|
||||||
|
println!("FUZZ_INPUT @ {:#x}", test_one_input_ptr);
|
||||||
|
let test_length_ptr = elf
|
||||||
|
.resolve_symbol("FUZZ_LENGTH", 0)
|
||||||
|
.expect("Symbol FUZZ_LENGTH not found");
|
||||||
|
let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin());
|
||||||
|
println!("FUZZ_LENGTH @ {:#x}", test_length_ptr);
|
||||||
|
let check_breakpoint = elf
|
||||||
|
.resolve_symbol("trigger_Qemu_break", 0)
|
||||||
|
.expect("Symbol trigger_Qemu_break not found");
|
||||||
|
let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin());
|
||||||
|
println!("Breakpoint at {:#x}", check_breakpoint);
|
||||||
|
|
||||||
|
|
||||||
|
//====== Note the input field
|
||||||
|
let input_addr = test_one_input_ptr;
|
||||||
|
println!("Placing input at {:#x}", input_addr);
|
||||||
|
emu.set_breakpoint(check_breakpoint); // trigger_Qemu_break
|
||||||
|
|
||||||
|
//====== Setup log and stdout
|
||||||
|
let log = RefCell::new(
|
||||||
|
OpenOptions::new()
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&logfile)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mut stdout_cpy = unsafe {
|
||||||
|
let new_fd = dup(io::stdout().as_raw_fd())?;
|
||||||
|
File::from_raw_fd(new_fd)
|
||||||
|
};
|
||||||
|
#[cfg(unix)]
|
||||||
|
let file_null = File::open("/dev/null")?;
|
||||||
|
|
||||||
|
//====== Create the most simple status display and managers.
|
||||||
|
|
||||||
|
// 'While the stats are state, they are usually used in the broker - which is likely never restarted
|
||||||
|
let monitor = SimpleMonitor::new(|s| {
|
||||||
|
#[cfg(unix)]
|
||||||
|
writeln!(&mut stdout_cpy, "{}", s).unwrap();
|
||||||
|
#[cfg(windows)]
|
||||||
|
println!("{}", s);
|
||||||
|
writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut shmem_provider = StdShMemProvider::new()?;
|
||||||
|
|
||||||
|
//====== Create the most simple status display and managers.
|
||||||
|
let mut mgr = SimpleEventManager::new(monitor);
|
||||||
|
|
||||||
|
// Create an observation channel using the coverage map
|
||||||
|
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||||
|
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||||
|
let edges_observer =
|
||||||
|
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let time_observer = TimeObserver::new("time");
|
||||||
|
|
||||||
|
// Create an observation channel using cmplog map
|
||||||
|
let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true);
|
||||||
|
|
||||||
|
// The state of the edges feedback.
|
||||||
|
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let feedback = feedback_or!(
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
||||||
|
MapHitIncreaseFeedback::new(),
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
let objective = CrashFeedback::new();
|
||||||
|
|
||||||
|
// create a State from scratch
|
||||||
|
let mut state = {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
OnDiskCorpus::new(corpus_dir).unwrap(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(objective_dir).unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// They are the data related to the feedbacks that you want to persist in the State.
|
||||||
|
tuple_list!(feedback_state),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let calibration = CalibrationStage::new(&mut state, &edges_observer);
|
||||||
|
|
||||||
|
// Setup a randomic Input2State stage
|
||||||
|
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||||
|
|
||||||
|
// Setup a MOPT mutator
|
||||||
|
let mutator = StdMOptMutator::new(&mut state, havoc_mutations().merge(tokens_mutations()), 5)?;
|
||||||
|
|
||||||
|
let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer);
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(PowerQueueCorpusScheduler::new());
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
|
let mut harness = |input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
|
if len > 32 {
|
||||||
|
buf = &buf[0..32];
|
||||||
|
len = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes());
|
||||||
|
emu.write_mem(input_addr,buf);
|
||||||
|
// println!("{:#?}",edges_copy);
|
||||||
|
|
||||||
|
emu.run();
|
||||||
|
// println!("{:#?}",edges_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
let executor = QemuExecutor::new(
|
||||||
|
&mut harness,
|
||||||
|
&emu,
|
||||||
|
tuple_list!(
|
||||||
|
QemuEdgeCoverageHelper::new(),
|
||||||
|
QemuCmpLogHelper::new(),
|
||||||
|
//QemuAsanHelper::new(),
|
||||||
|
QemuSysSnapshotHelper::new()
|
||||||
|
),
|
||||||
|
tuple_list!(edges_observer, time_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
||||||
|
let executor = TimeoutExecutor::new(executor, timeout);
|
||||||
|
// Show the cmplog observer
|
||||||
|
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
|
||||||
|
|
||||||
|
// Read tokens
|
||||||
|
if let Some(tokenfile) = tokenfile {
|
||||||
|
if state.metadata().get::<Tokens>().is_none() {
|
||||||
|
state.add_metadata(Tokens::from_tokens_file(tokenfile)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &seed_dir);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracing = ShadowTracingStage::new(&mut executor);
|
||||||
|
|
||||||
|
// The order of the stages matter!
|
||||||
|
let mut stages = tuple_list!(calibration, tracing, i2s, power);
|
||||||
|
|
||||||
|
// Remove target ouput (logs still survive)
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
let null_fd = file_null.as_raw_fd();
|
||||||
|
dup2(null_fd, io::stdout().as_raw_fd())?;
|
||||||
|
dup2(null_fd, io::stderr().as_raw_fd())?;
|
||||||
|
}
|
||||||
|
// reopen file to make sure we're at the end
|
||||||
|
log.replace(
|
||||||
|
OpenOptions::new()
|
||||||
|
.append(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&logfile)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.expect("Error in the fuzzing loop");
|
||||||
|
|
||||||
|
// Never reached
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
// pub mod fuzzer;
|
pub mod fuzzer;
|
||||||
pub mod showmap;
|
pub mod showmap;
|
||||||
pub mod worst;
|
pub mod worst;
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
@ -9,25 +9,8 @@ use libafl_qemu::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// let mut args2: Vec<String> = vec![
|
#[cfg(all(target_os = "linux", feature = "showmap"))]
|
||||||
// "qemu-system-arm",
|
showmap::main();
|
||||||
// "-machine","mps2-an385",
|
#[cfg(all(target_os = "linux", not(feature = "showmap")))]
|
||||||
// "-monitor", "null",
|
fuzzer::main();
|
||||||
// "-semihosting",
|
|
||||||
// "--semihosting-config", "enable=on,target=native",
|
|
||||||
// "-kernel", "RTOSDemo.axf",
|
|
||||||
// "-serial", "stdio", "-nographic",
|
|
||||||
// "-snapshot", "-drive", "if=none,format=qcow2,file=dummy.qcow2",
|
|
||||||
// "-S"
|
|
||||||
// ].iter().map(|x| x.to_string()).collect();
|
|
||||||
// init(&mut args2, &Vec::new());
|
|
||||||
// let succ = emu::snapshot_save("Start");
|
|
||||||
// println!("Snaphsot: {}",succ);
|
|
||||||
// emu::set_breakpoint(0x00004f5c);
|
|
||||||
// emu::run();
|
|
||||||
// let succ = emu::snapshot_load("Start");
|
|
||||||
// emu::run();
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
// fuzzer::main()
|
|
||||||
showmap::main()
|
|
||||||
}
|
}
|
||||||
|
@ -216,3 +216,61 @@ impl Default for HitFeedback {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
|
||||||
|
|
||||||
|
/// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct MapHitIncreaseFeedback {
|
||||||
|
record_high : u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, S> Feedback<I, S> for MapHitIncreaseFeedback
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
S: HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &I,
|
||||||
|
_observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<I>,
|
||||||
|
OT: ObserversTuple<I, S>,
|
||||||
|
{
|
||||||
|
let observer = _observers.match_name::<HitcountsMapObserver<VariableMapObserver<u8>>>("edges")
|
||||||
|
.expect("HitcountsMapObserver not found");
|
||||||
|
let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64));
|
||||||
|
if cur > self.record_high {
|
||||||
|
self.record_high = cur;
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for MapHitIncreaseFeedback {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"HitFeedback"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapHitIncreaseFeedback {
|
||||||
|
/// Creates a new [`HitFeedback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {record_high: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MapHitIncreaseFeedback {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user