Remove base-pattern for Monitor (#2953)

* Remove base-pattern for Monitor

* Fix runtime parameter for log_record

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
EvianZhang 2025-02-08 21:08:00 +08:00 committed by GitHub
parent 075fb0daa2
commit abe955137f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 44 additions and 80 deletions

View File

@ -10,6 +10,7 @@
- There is a `ClientStatsManager` to manage client statistics, and is owned by `EventManager`. Most of previous `Monitor`'s trait methods have been moved to the `ClientStatsManager`. - There is a `ClientStatsManager` to manage client statistics, and is owned by `EventManager`. Most of previous `Monitor`'s trait methods have been moved to the `ClientStatsManager`.
- `user_monitor` has been renamed to `user_stats`, and its structure definitions have been moved to `statistics` module as well. - `user_monitor` has been renamed to `user_stats`, and its structure definitions have been moved to `statistics` module as well.
- `introspection_monitor` has been renamed to `introspection_stats`, and perf-related structure definitions have been renamed and moved to `statistics` module as well. - `introspection_monitor` has been renamed to `introspection_stats`, and perf-related structure definitions have been renamed and moved to `statistics` module as well.
- `OnDiskTomlMonitor`, `OnDiskJsonMonitor`, `OnDiskJsonAggregateMonitor` are now no longer takes a base monitor to wrap. If you want to use multiple monitors together, use `libafl::combine_monitor!`.
# 0.14.1 -> 0.15.0 # 0.14.1 -> 0.15.0
- `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef<Path>` instead of a byte array for the filename/id. - `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef<Path>` instead of a byte array for the filename/id.

View File

@ -7,6 +7,7 @@ use std::{env, net::SocketAddr, path::PathBuf};
use clap::{self, Parser}; use clap::{self, Parser};
use libafl::{ use libafl::{
combine_monitor,
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::{launcher::Launcher, EventConfig}, events::{launcher::Launcher, EventConfig},
executors::{inprocess::InProcessExecutor, ExitKind}, executors::{inprocess::InProcessExecutor, ExitKind},
@ -140,9 +141,9 @@ pub extern "C" fn libafl_main() {
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let monitor = OnDiskTomlMonitor::new( let monitor = combine_monitor!(
"./fuzzer_stats.toml", OnDiskTomlMonitor::new("./fuzzer_stats.toml"),
MultiMonitor::new(|s| println!("{s}")), MultiMonitor::new(|s| println!("{s}"))
); );
let mut run_client = |state: Option<_>, mut restarting_mgr, _client_description| { let mut run_client = |state: Option<_>, mut restarting_mgr, _client_description| {

View File

@ -8,6 +8,7 @@ use std::{env, net::SocketAddr, path::PathBuf};
use clap::Parser; use clap::Parser;
use libafl::{ use libafl::{
combine_monitor,
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
events::{ events::{
launcher::{ClientDescription, Launcher}, launcher::{ClientDescription, Launcher},
@ -156,9 +157,9 @@ pub extern "C" fn libafl_main() {
let shmem_provider = MmapShMemProvider::new().expect("Failed to init shared memory"); let shmem_provider = MmapShMemProvider::new().expect("Failed to init shared memory");
let monitor = OnDiskTomlMonitor::new( let monitor = combine_monitor!(
"./fuzzer_stats.toml", OnDiskTomlMonitor::new("./fuzzer_stats.toml"),
MultiMonitor::new(|s| println!("{s}")), MultiMonitor::new(|s| println!("{s}"))
); );
let mut run_client = |state: Option<_>, let mut run_client = |state: Option<_>,

View File

@ -1,4 +1,4 @@
//! Monitors that wrap a base monitor and also log to disk using different formats like `JSON` and `TOML`. //! Monitors that log to disk using different formats like `JSON` and `TOML`.
use alloc::string::String; use alloc::string::String;
use core::time::Duration; use core::time::Duration;
@ -11,32 +11,22 @@ use std::{
use libafl_bolts::{current_time, format_duration_hms, ClientId}; use libafl_bolts::{current_time, format_duration_hms, ClientId};
use serde_json::json; use serde_json::json;
use crate::{ use crate::{monitors::Monitor, statistics::manager::ClientStatsManager};
monitors::{Monitor, NopMonitor},
statistics::manager::ClientStatsManager,
};
/// Wrap a monitor and log the current state of the monitor into a Toml file. /// Wrap a monitor and log the current state of the monitor into a Toml file.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OnDiskTomlMonitor<M> pub struct OnDiskTomlMonitor {
where
M: Monitor,
{
base: M,
filename: PathBuf, filename: PathBuf,
last_update: Duration, last_update: Duration,
update_interval: Duration, update_interval: Duration,
} }
impl<M> Monitor for OnDiskTomlMonitor<M> impl Monitor for OnDiskTomlMonitor {
where
M: Monitor,
{
fn display( fn display(
&mut self, &mut self,
client_stats_manager: &mut ClientStatsManager, client_stats_manager: &mut ClientStatsManager,
event_msg: &str, _event_msg: &str,
sender_id: ClientId, _sender_id: ClientId,
) { ) {
let cur_time = current_time(); let cur_time = current_time();
@ -100,33 +90,26 @@ exec_sec = {}
drop(file); drop(file);
} }
self.base
.display(client_stats_manager, event_msg, sender_id);
} }
} }
impl<M> OnDiskTomlMonitor<M> impl OnDiskTomlMonitor {
where
M: Monitor,
{
/// Create new [`OnDiskTomlMonitor`] /// Create new [`OnDiskTomlMonitor`]
#[must_use] #[must_use]
pub fn new<P>(filename: P, base: M) -> Self pub fn new<P>(filename: P) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
Self::with_update_interval(filename, base, Duration::from_secs(60)) Self::with_update_interval(filename, Duration::from_secs(60))
} }
/// Create new [`OnDiskTomlMonitor`] with custom update interval /// Create new [`OnDiskTomlMonitor`] with custom update interval
#[must_use] #[must_use]
pub fn with_update_interval<P>(filename: P, base: M, update_interval: Duration) -> Self pub fn with_update_interval<P>(filename: P, update_interval: Duration) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
Self { Self {
base,
filename: filename.into(), filename: filename.into(),
last_update: current_time() - update_interval, last_update: current_time() - update_interval,
update_interval, update_interval,
@ -134,62 +117,55 @@ where
} }
} }
impl OnDiskTomlMonitor<NopMonitor> { impl OnDiskTomlMonitor {
/// Create new [`OnDiskTomlMonitor`] without a base /// Create new [`OnDiskTomlMonitor`] without a base
#[must_use] #[must_use]
#[deprecated(since = "0.16.0", note = "Use new directly")]
pub fn nop<P>(filename: P) -> Self pub fn nop<P>(filename: P) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
Self::new(filename, NopMonitor::new()) Self::new(filename)
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Wraps a base monitor and continuously appends the current statistics to a Json lines file. /// Continuously appends the current statistics to a Json lines file.
pub struct OnDiskJsonMonitor<F, M> pub struct OnDiskJsonMonitor<F>
where where
F: FnMut(&mut M) -> bool, F: FnMut(&mut ClientStatsManager) -> bool,
M: Monitor,
{ {
base: M,
path: PathBuf, path: PathBuf,
/// A function that has the current runtime as argument and decides, whether a record should be logged /// A function that has the current runtime as argument and decides, whether a record should be logged
log_record: F, log_record: F,
} }
impl<F, M> OnDiskJsonMonitor<F, M> impl<F> OnDiskJsonMonitor<F>
where where
F: FnMut(&mut M) -> bool, F: FnMut(&mut ClientStatsManager) -> bool,
M: Monitor,
{ {
/// Create a new [`OnDiskJsonMonitor`] /// Create a new [`OnDiskJsonMonitor`]
pub fn new<P>(filename: P, base: M, log_record: F) -> Self pub fn new<P>(filename: P, log_record: F) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
let path = filename.into(); let path = filename.into();
Self { Self { path, log_record }
base,
path,
log_record,
}
} }
} }
impl<F, M> Monitor for OnDiskJsonMonitor<F, M> impl<F> Monitor for OnDiskJsonMonitor<F>
where where
F: FnMut(&mut M) -> bool, F: FnMut(&mut ClientStatsManager) -> bool,
M: Monitor,
{ {
fn display( fn display(
&mut self, &mut self,
client_stats_manager: &mut ClientStatsManager, client_stats_manager: &mut ClientStatsManager,
event_msg: &str, _event_msg: &str,
sender_id: ClientId, _sender_id: ClientId,
) { ) {
if (self.log_record)(&mut self.base) { if (self.log_record)(client_stats_manager) {
let file = OpenOptions::new() let file = OpenOptions::new()
.append(true) .append(true)
.create(true) .create(true)
@ -207,7 +183,5 @@ where
}); });
writeln!(&file, "{line}").expect("Unable to write Json to file"); writeln!(&file, "{line}").expect("Unable to write Json to file");
} }
self.base
.display(client_stats_manager, event_msg, sender_id);
} }
} }

View File

@ -11,22 +11,17 @@ use serde_json::json;
use crate::{monitors::Monitor, statistics::manager::ClientStatsManager}; use crate::{monitors::Monitor, statistics::manager::ClientStatsManager};
/// A monitor that wraps another monitor and logs aggregated stats to a JSON file. /// A monitor that logs aggregated stats to a JSON file.
#[derive(Clone)] #[derive(Clone)]
pub struct OnDiskJsonAggregateMonitor<M> { pub struct OnDiskJsonAggregateMonitor {
base: M,
json_path: PathBuf, json_path: PathBuf,
last_update: Duration, last_update: Duration,
update_interval: Duration, update_interval: Duration,
} }
impl<M> Debug for OnDiskJsonAggregateMonitor<M> impl Debug for OnDiskJsonAggregateMonitor {
where
M: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("OnDiskJsonAggregateMonitor") f.debug_struct("OnDiskJsonAggregateMonitor")
.field("base", &self.base)
.field("last_update", &self.last_update) .field("last_update", &self.last_update)
.field("update_interval", &self.update_interval) .field("update_interval", &self.update_interval)
.field("json_path", &self.json_path) .field("json_path", &self.json_path)
@ -34,20 +29,13 @@ where
} }
} }
impl<M> Monitor for OnDiskJsonAggregateMonitor<M> impl Monitor for OnDiskJsonAggregateMonitor {
where
M: Monitor,
{
fn display( fn display(
&mut self, &mut self,
client_stats_manager: &mut ClientStatsManager, client_stats_manager: &mut ClientStatsManager,
event_msg: &str, _event_msg: &str,
sender_id: ClientId, _sender_id: ClientId,
) { ) {
// First let the base monitor handle its display
self.base
.display(client_stats_manager, event_msg, sender_id);
// Write JSON stats if update interval has elapsed // Write JSON stats if update interval has elapsed
let cur_time = current_time(); let cur_time = current_time();
if cur_time - self.last_update >= self.update_interval { if cur_time - self.last_update >= self.update_interval {
@ -83,22 +71,21 @@ where
} }
} }
impl<M> OnDiskJsonAggregateMonitor<M> { impl OnDiskJsonAggregateMonitor {
/// Creates a new [`OnDiskJsonAggregateMonitor`] /// Creates a new [`OnDiskJsonAggregateMonitor`]
pub fn new<P>(base: M, json_path: P) -> Self pub fn new<P>(json_path: P) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
Self::with_interval(json_path, base, Duration::from_secs(10)) Self::with_interval(json_path, Duration::from_secs(10))
} }
/// Creates a new [`OnDiskJsonAggregateMonitor`] with custom update interval /// Creates a new [`OnDiskJsonAggregateMonitor`] with custom update interval
pub fn with_interval<P>(json_path: P, base: M, update_interval: Duration) -> Self pub fn with_interval<P>(json_path: P, update_interval: Duration) -> Self
where where
P: Into<PathBuf>, P: Into<PathBuf>,
{ {
Self { Self {
base,
json_path: json_path.into(), json_path: json_path.into(),
last_update: current_time() - update_interval, last_update: current_time() - update_interval,
update_interval, update_interval,