Implement AflMap (#416)
* aflmap * nits * nits * switch implementation * clippy * set fuzzbench fuzzer to afl map * fix monitor display * Remove MapFindFilter and fix names * AndReducer * fixed testcase * always inline * remove inline(always) Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com> Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
parent
6e59e5bdc7
commit
1f24ad0b65
@ -28,7 +28,7 @@ use libafl::{
|
|||||||
events::SimpleRestartingEventManager,
|
events::SimpleRestartingEventManager,
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
feedback_or,
|
feedback_or,
|
||||||
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
|
feedbacks::{AflMapFeedback, CrashFeedback, MapFeedbackState, TimeFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
monitors::SimpleMonitor,
|
monitors::SimpleMonitor,
|
||||||
@ -37,7 +37,7 @@ use libafl::{
|
|||||||
token_mutations::I2SRandReplace,
|
token_mutations::I2SRandReplace,
|
||||||
tokens_mutations, Tokens,
|
tokens_mutations, Tokens,
|
||||||
},
|
},
|
||||||
observers::{StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
stages::{
|
stages::{
|
||||||
calibrate::CalibrationStage,
|
calibrate::CalibrationStage,
|
||||||
power::{PowerMutationalStage, PowerSchedule},
|
power::{PowerMutationalStage, PowerSchedule},
|
||||||
@ -206,7 +206,7 @@ fn fuzz(
|
|||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
|
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
|
||||||
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
|
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
|
||||||
let edges_observer = StdMapObserver::new("edges", edges);
|
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
// Create an observation channel to keep track of the execution time
|
||||||
let time_observer = TimeObserver::new("time");
|
let time_observer = TimeObserver::new("time");
|
||||||
@ -221,7 +221,7 @@ fn fuzz(
|
|||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
let feedback = feedback_or!(
|
let feedback = feedback_or!(
|
||||||
// New maximization map feedback linked to the edges observer and the feedback state
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
AflMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
|
||||||
// Time feedback, this one does not need a feedback state
|
// Time feedback, this one does not need a feedback state
|
||||||
TimeFeedback::new_with_observer(&time_observer)
|
TimeFeedback::new_with_observer(&time_observer)
|
||||||
);
|
);
|
||||||
|
@ -21,19 +21,22 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A [`MapFeedback`] that implements the AFL algorithm using an [`OrReducer`] combining the bits for the history map and the bit from ``HitcountsMapObserver``.
|
||||||
|
pub type AflMapFeedback<FT, I, O, S, T> = MapFeedback<FT, I, DifferentIsNovel, O, OrReducer, S, T>;
|
||||||
|
|
||||||
/// A [`MapFeedback`] that strives to maximize the map contents.
|
/// A [`MapFeedback`] that strives to maximize the map contents.
|
||||||
pub type MaxMapFeedback<FT, I, O, S, T> = MapFeedback<FT, I, MapNopFilter, O, MaxReducer, S, T>;
|
pub type MaxMapFeedback<FT, I, O, S, T> = MapFeedback<FT, I, DifferentIsNovel, O, MaxReducer, S, T>;
|
||||||
/// A [`MapFeedback`] that strives to minimize the map contents.
|
/// A [`MapFeedback`] that strives to minimize the map contents.
|
||||||
pub type MinMapFeedback<FT, I, O, S, T> = MapFeedback<FT, I, MapNopFilter, O, MinReducer, S, T>;
|
pub type MinMapFeedback<FT, I, O, S, T> = MapFeedback<FT, I, DifferentIsNovel, O, MinReducer, S, T>;
|
||||||
|
|
||||||
/// A [`MapFeedback`] that strives to maximize the map contents,
|
/// A [`MapFeedback`] that strives to maximize the map contents,
|
||||||
/// but only, if a value is larger than `pow2` of the previous.
|
/// but only, if a value is larger than `pow2` of the previous.
|
||||||
pub type MaxMapPow2Feedback<FT, I, O, S, T> =
|
pub type MaxMapPow2Feedback<FT, I, O, S, T> =
|
||||||
MapFeedback<FT, I, MaxMapPow2Filter, O, MaxReducer, S, T>;
|
MapFeedback<FT, I, NextPow2IsNovel, O, MaxReducer, S, T>;
|
||||||
/// A [`MapFeedback`] that strives to maximize the map contents,
|
/// A [`MapFeedback`] that strives to maximize the map contents,
|
||||||
/// but only, if a value is larger than `pow2` of the previous.
|
/// but only, if a value is larger than `pow2` of the previous.
|
||||||
pub type MaxMapOneOrFilledFeedback<FT, I, O, S, T> =
|
pub type MaxMapOneOrFilledFeedback<FT, I, O, S, T> =
|
||||||
MapFeedback<FT, I, MaxMapOneOrFilledFilter, O, MaxReducer, S, T>;
|
MapFeedback<FT, I, OneOrFilledIsNovel, O, MaxReducer, S, T>;
|
||||||
|
|
||||||
/// A `Reducer` function is used to aggregate values for the novelty search
|
/// A `Reducer` function is used to aggregate values for the novelty search
|
||||||
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static
|
pub trait Reducer<T>: Serialize + serde::de::DeserializeOwned + 'static
|
||||||
@ -44,6 +47,46 @@ where
|
|||||||
fn reduce(first: T, second: T) -> T;
|
fn reduce(first: T, second: T) -> T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`OrReducer`] reduces the values returning the bitwise OR with the old value
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct OrReducer {}
|
||||||
|
|
||||||
|
impl<T> Reducer<T> for OrReducer
|
||||||
|
where
|
||||||
|
T: PrimInt
|
||||||
|
+ Default
|
||||||
|
+ Copy
|
||||||
|
+ 'static
|
||||||
|
+ serde::Serialize
|
||||||
|
+ serde::de::DeserializeOwned
|
||||||
|
+ PartialOrd,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn reduce(history: T, new: T) -> T {
|
||||||
|
history | new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`AndReducer`] reduces the values returning the bitwise AND with the old value
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct AndReducer {}
|
||||||
|
|
||||||
|
impl<T> Reducer<T> for AndReducer
|
||||||
|
where
|
||||||
|
T: PrimInt
|
||||||
|
+ Default
|
||||||
|
+ Copy
|
||||||
|
+ 'static
|
||||||
|
+ serde::Serialize
|
||||||
|
+ serde::de::DeserializeOwned
|
||||||
|
+ PartialOrd,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn reduce(history: T, new: T) -> T {
|
||||||
|
history & new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`MaxReducer`] reduces int values and returns their maximum.
|
/// A [`MaxReducer`] reduces int values and returns their maximum.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct MaxReducer {}
|
pub struct MaxReducer {}
|
||||||
@ -92,28 +135,26 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `MapFindFilter` function gets called after the `MapFeedback` found a new entry.
|
/// A `IsNovel` function is used to discriminate if a reduced value is considered novel.
|
||||||
pub trait MapFindFilter<T>: Serialize + serde::de::DeserializeOwned + 'static
|
pub trait IsNovel<T>: Serialize + serde::de::DeserializeOwned + 'static
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
/// If a new value in the [`MapFeedback`] was found,
|
/// If a new value in the [`MapFeedback`] was found,
|
||||||
/// this filter can decide if the result is intersting or not.
|
/// this filter can decide if the result is considered novel or not.
|
||||||
/// This way, you can restrict the finds further.
|
fn is_novel(old: T, new: T) -> bool;
|
||||||
fn is_interesting(old: T, new: T) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A filter that never filters out any finds.
|
/// [`AllIsNovel`] consider everything a novelty. Here mostly just for debugging.
|
||||||
/// The default
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct MapNopFilter {}
|
pub struct AllIsNovel {}
|
||||||
|
|
||||||
impl<T> MapFindFilter<T> for MapNopFilter
|
impl<T> IsNovel<T> for AllIsNovel
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_interesting(_old: T, _new: T) -> bool {
|
fn is_novel(_old: T, _new: T) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,15 +173,28 @@ fn saturating_next_power_of_two<T: PrimInt>(n: T) -> T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A filter that only saves values which are at least the next pow2 class
|
/// Consider as novelty if the reduced value is different from the old value.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct MaxMapPow2Filter {}
|
pub struct DifferentIsNovel {}
|
||||||
impl<T> MapFindFilter<T> for MaxMapPow2Filter
|
impl<T> IsNovel<T> for DifferentIsNovel
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_interesting(old: T, new: T) -> bool {
|
fn is_novel(old: T, new: T) -> bool {
|
||||||
|
old != new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only consider as novel the values which are at least the next pow2 class of the old value
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct NextPow2IsNovel {}
|
||||||
|
impl<T> IsNovel<T> for NextPow2IsNovel
|
||||||
|
where
|
||||||
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_novel(old: T, new: T) -> bool {
|
||||||
// We use a trait so we build our numbers from scratch here.
|
// We use a trait so we build our numbers from scratch here.
|
||||||
// This way it works with Nums of any size.
|
// This way it works with Nums of any size.
|
||||||
if new <= old {
|
if new <= old {
|
||||||
@ -154,14 +208,14 @@ where
|
|||||||
|
|
||||||
/// A filter that only saves values which are at least the next pow2 class
|
/// A filter that only saves values which are at least the next pow2 class
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct MaxMapOneOrFilledFilter {}
|
pub struct OneOrFilledIsNovel {}
|
||||||
impl<T> MapFindFilter<T> for MaxMapOneOrFilledFilter
|
impl<T> IsNovel<T> for OneOrFilledIsNovel
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_interesting(old: T, new: T) -> bool {
|
fn is_novel(old: T, new: T) -> bool {
|
||||||
(new == T::one() || new == T::one() || new == T::max_value()) && new > old
|
(new == T::one() || new == T::max_value()) && new > old
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,12 +353,12 @@ where
|
|||||||
/// The most common AFL-like feedback type
|
/// The most common AFL-like feedback type
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
pub struct MapFeedback<FT, I, MF, O, R, S, T>
|
pub struct MapFeedback<FT, I, N, O, R, S, T>
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
MF: MapFindFilter<T>,
|
N: IsNovel<T>,
|
||||||
S: HasFeedbackStates<FT>,
|
S: HasFeedbackStates<FT>,
|
||||||
FT: FeedbackStatesTuple,
|
FT: FeedbackStatesTuple,
|
||||||
{
|
{
|
||||||
@ -317,15 +371,15 @@ where
|
|||||||
/// Name identifier of the observer
|
/// Name identifier of the observer
|
||||||
observer_name: String,
|
observer_name: String,
|
||||||
/// Phantom Data of Reducer
|
/// Phantom Data of Reducer
|
||||||
phantom: PhantomData<(FT, I, MF, S, R, O, T)>,
|
phantom: PhantomData<(FT, I, N, S, R, O, T)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FT, I, MF, O, R, S, T> Feedback<I, S> for MapFeedback<FT, I, MF, O, R, S, T>
|
impl<FT, I, N, O, R, S, T> Feedback<I, S> for MapFeedback<FT, I, N, O, R, S, T>
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
MF: MapFindFilter<T>,
|
N: IsNovel<T>,
|
||||||
I: Input,
|
I: Input,
|
||||||
S: HasFeedbackStates<FT> + HasClientPerfMonitor,
|
S: HasFeedbackStates<FT> + HasClientPerfMonitor,
|
||||||
FT: FeedbackStatesTuple,
|
FT: FeedbackStatesTuple,
|
||||||
@ -363,7 +417,7 @@ where
|
|||||||
let item = *observer.get(i);
|
let item = *observer.get(i);
|
||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced && MF::is_interesting(history, reduced) {
|
if N::is_novel(history, reduced) {
|
||||||
map_state.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
self.novelties.as_mut().unwrap().push(i);
|
self.novelties.as_mut().unwrap().push(i);
|
||||||
@ -375,7 +429,7 @@ where
|
|||||||
let item = *observer.get(i);
|
let item = *observer.get(i);
|
||||||
|
|
||||||
let reduced = R::reduce(history, item);
|
let reduced = R::reduce(history, item);
|
||||||
if history != reduced && MF::is_interesting(history, reduced) {
|
if N::is_novel(history, reduced) {
|
||||||
map_state.history_map[i] = reduced;
|
map_state.history_map[i] = reduced;
|
||||||
interesting = true;
|
interesting = true;
|
||||||
}
|
}
|
||||||
@ -429,11 +483,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FT, I, MF, O, R, S, T> Named for MapFeedback<FT, I, MF, O, R, S, T>
|
impl<FT, I, N, O, R, S, T> Named for MapFeedback<FT, I, N, O, R, S, T>
|
||||||
where
|
where
|
||||||
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
T: PrimInt + Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned + Debug,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
MF: MapFindFilter<T>,
|
N: IsNovel<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
S: HasFeedbackStates<FT>,
|
S: HasFeedbackStates<FT>,
|
||||||
FT: FeedbackStatesTuple,
|
FT: FeedbackStatesTuple,
|
||||||
@ -444,7 +498,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<FT, I, MF, O, R, S, T> MapFeedback<FT, I, MF, O, R, S, T>
|
impl<FT, I, N, O, R, S, T> MapFeedback<FT, I, N, O, R, S, T>
|
||||||
where
|
where
|
||||||
T: PrimInt
|
T: PrimInt
|
||||||
+ Default
|
+ Default
|
||||||
@ -455,7 +509,7 @@ where
|
|||||||
+ PartialOrd
|
+ PartialOrd
|
||||||
+ Debug,
|
+ Debug,
|
||||||
R: Reducer<T>,
|
R: Reducer<T>,
|
||||||
MF: MapFindFilter<T>,
|
N: IsNovel<T>,
|
||||||
O: MapObserver<T>,
|
O: MapObserver<T>,
|
||||||
S: HasFeedbackStates<FT>,
|
S: HasFeedbackStates<FT>,
|
||||||
FT: FeedbackStatesTuple,
|
FT: FeedbackStatesTuple,
|
||||||
@ -614,25 +668,25 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::feedbacks::{MapFindFilter, MapNopFilter, MaxMapPow2Filter};
|
use crate::feedbacks::{AllIsNovel, IsNovel, NextPow2IsNovel};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_map_max_pow2_filter() {
|
fn test_map_is_novel() {
|
||||||
// sanity check
|
// sanity check
|
||||||
assert!(MapNopFilter::is_interesting(0_u8, 0));
|
assert!(AllIsNovel::is_novel(0_u8, 0));
|
||||||
|
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(0_u8, 0));
|
assert!(!NextPow2IsNovel::is_novel(0_u8, 0));
|
||||||
assert!(MaxMapPow2Filter::is_interesting(0_u8, 1));
|
assert!(NextPow2IsNovel::is_novel(0_u8, 1));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(1_u8, 1));
|
assert!(!NextPow2IsNovel::is_novel(1_u8, 1));
|
||||||
assert!(MaxMapPow2Filter::is_interesting(1_u8, 2));
|
assert!(NextPow2IsNovel::is_novel(1_u8, 2));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(2_u8, 2));
|
assert!(!NextPow2IsNovel::is_novel(2_u8, 2));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(2_u8, 3));
|
assert!(!NextPow2IsNovel::is_novel(2_u8, 3));
|
||||||
assert!(MaxMapPow2Filter::is_interesting(2_u8, 4));
|
assert!(NextPow2IsNovel::is_novel(2_u8, 4));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(128_u8, 128));
|
assert!(!NextPow2IsNovel::is_novel(128_u8, 128));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(129_u8, 128));
|
assert!(!NextPow2IsNovel::is_novel(129_u8, 128));
|
||||||
assert!(MaxMapPow2Filter::is_interesting(128_u8, 255));
|
assert!(NextPow2IsNovel::is_novel(128_u8, 255));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(255_u8, 128));
|
assert!(!NextPow2IsNovel::is_novel(255_u8, 128));
|
||||||
assert!(MaxMapPow2Filter::is_interesting(254_u8, 255));
|
assert!(NextPow2IsNovel::is_novel(254_u8, 255));
|
||||||
assert!(!MaxMapPow2Filter::is_interesting(255_u8, 255));
|
assert!(!NextPow2IsNovel::is_novel(255_u8, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,19 +291,19 @@ where
|
|||||||
|
|
||||||
fn display(&mut self, event_msg: String, sender_id: u32) {
|
fn display(&mut self, event_msg: String, sender_id: u32) {
|
||||||
let fmt = format!(
|
let fmt = format!(
|
||||||
"[{} #{}] run time: {}, clients: {}, corpus: {}, objectives: {}, executions: {}, {} exec/sec: {}",
|
"[{} #{}] run time: {}, clients: {}, corpus: {}, objectives: {}, executions: {}{}, exec/sec: {}",
|
||||||
event_msg,
|
event_msg,
|
||||||
sender_id,
|
sender_id,
|
||||||
format_duration_hms(&(current_time() - self.start_time)),
|
format_duration_hms(&(current_time() - self.start_time)),
|
||||||
self.client_stats().len(),
|
self.client_stats().len(),
|
||||||
self.corpus_size(),
|
self.corpus_size(),
|
||||||
self.objective_size(),
|
self.objective_size(),
|
||||||
|
self.total_execs(),
|
||||||
if let Some(stability) = self.stability() {
|
if let Some(stability) = self.stability() {
|
||||||
format!(", stability: {:.2}", stability)
|
format!(", stability: {:.2}", stability)
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
},
|
},
|
||||||
self.total_execs(),
|
|
||||||
self.execs_per_sec()
|
self.execs_per_sec()
|
||||||
);
|
);
|
||||||
(self.print_fn)(fmt);
|
(self.print_fn)(fmt);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user