Skip to content

Commit c398400

Browse files
riesentoastermzfr
authored andcommitted
Add OnDiskJsonAggregateMonitor (AFLplusplus#2845)
* Add OnDiskJsonAggregateMonitor * Fix formatting * Remove unnecessary trait bounds * Remove unnecessary import restriction * Remove unnecessary imports * fix formatting
1 parent d1b432a commit c398400

File tree

2 files changed

+145
-8
lines changed

2 files changed

+145
-8
lines changed

libafl/src/monitors/disk_aggregate.rs

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//! Monitors that log aggregated stats to disk.
2+
3+
use alloc::vec::Vec;
4+
use core::{
5+
fmt::{Debug, Formatter},
6+
time::Duration,
7+
};
8+
use std::{fs::OpenOptions, io::Write, path::PathBuf};
9+
10+
use libafl_bolts::{current_time, ClientId};
11+
use serde_json::json;
12+
13+
use crate::monitors::{Aggregator, ClientStats, Monitor};
14+
15+
/// A monitor that wraps another monitor and logs aggregated stats to a JSON file.
16+
#[derive(Clone)]
17+
pub struct OnDiskJsonAggregateMonitor<M> {
18+
base: M,
19+
aggregator: Aggregator,
20+
json_path: PathBuf,
21+
last_update: Duration,
22+
update_interval: Duration,
23+
}
24+
25+
impl<M> Debug for OnDiskJsonAggregateMonitor<M>
26+
where
27+
M: Debug,
28+
{
29+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
30+
f.debug_struct("OnDiskJsonAggregateMonitor")
31+
.field("base", &self.base)
32+
.field("last_update", &self.last_update)
33+
.field("update_interval", &self.update_interval)
34+
.field("json_path", &self.json_path)
35+
.finish_non_exhaustive()
36+
}
37+
}
38+
39+
impl<M> Monitor for OnDiskJsonAggregateMonitor<M>
40+
where
41+
M: Monitor,
42+
{
43+
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats> {
44+
self.base.client_stats_mut()
45+
}
46+
47+
fn client_stats(&self) -> &[ClientStats] {
48+
self.base.client_stats()
49+
}
50+
51+
fn set_start_time(&mut self, time: Duration) {
52+
self.base.set_start_time(time);
53+
}
54+
55+
fn start_time(&self) -> Duration {
56+
self.base.start_time()
57+
}
58+
59+
fn aggregate(&mut self, name: &str) {
60+
self.aggregator.aggregate(name, self.base.client_stats());
61+
self.base.aggregate(name);
62+
}
63+
64+
fn display(&mut self, event_msg: &str, sender_id: ClientId) {
65+
// First let the base monitor handle its display
66+
self.base.display(event_msg, sender_id);
67+
68+
// Write JSON stats if update interval has elapsed
69+
let cur_time = current_time();
70+
if cur_time - self.last_update >= self.update_interval {
71+
self.last_update = cur_time;
72+
73+
let file = OpenOptions::new()
74+
.append(true)
75+
.create(true)
76+
.open(&self.json_path)
77+
.expect("Failed to open JSON logging file");
78+
79+
let mut json_value = json!({
80+
"run_time": (cur_time - self.start_time()).as_secs(),
81+
"clients": self.client_stats_count(),
82+
"corpus": self.corpus_size(),
83+
"objectives": self.objective_size(),
84+
"executions": self.total_execs(),
85+
"exec_sec": self.execs_per_sec(),
86+
});
87+
88+
// Add all aggregated values directly to the root
89+
if let Some(obj) = json_value.as_object_mut() {
90+
obj.extend(
91+
self.aggregator
92+
.aggregated
93+
.iter()
94+
.map(|(k, v)| (k.clone(), json!(v))),
95+
);
96+
}
97+
98+
writeln!(&file, "{json_value}").expect("Unable to write JSON to file");
99+
}
100+
}
101+
}
102+
103+
impl<M> OnDiskJsonAggregateMonitor<M> {
104+
/// Creates a new [`OnDiskJsonAggregateMonitor`]
105+
pub fn new<P>(base: M, json_path: P) -> Self
106+
where
107+
P: Into<PathBuf>,
108+
{
109+
Self::with_interval(json_path, base, Duration::from_secs(10))
110+
}
111+
112+
/// Creates a new [`OnDiskJsonAggregateMonitor`] with custom update interval
113+
pub fn with_interval<P>(json_path: P, base: M, update_interval: Duration) -> Self
114+
where
115+
P: Into<PathBuf>,
116+
{
117+
Self {
118+
base,
119+
aggregator: Aggregator::new(),
120+
json_path: json_path.into(),
121+
last_update: current_time() - update_interval,
122+
update_interval,
123+
}
124+
}
125+
}

libafl/src/monitors/mod.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,36 @@
33
pub mod multi;
44
pub use multi::MultiMonitor;
55

6+
#[cfg(feature = "std")]
7+
pub mod disk;
8+
#[cfg(feature = "std")]
9+
pub use disk::{OnDiskJsonMonitor, OnDiskTomlMonitor};
10+
11+
#[cfg(feature = "std")]
12+
pub mod disk_aggregate;
13+
#[cfg(feature = "std")]
14+
pub use disk_aggregate::OnDiskJsonAggregateMonitor;
15+
616
#[cfg(all(feature = "tui_monitor", feature = "std"))]
717
pub mod tui;
18+
#[cfg(all(feature = "tui_monitor", feature = "std"))]
19+
pub use tui::TuiMonitor;
820

921
#[cfg(all(feature = "prometheus_monitor", feature = "std"))]
1022
pub mod prometheus;
11-
use alloc::string::ToString;
1223

13-
#[cfg(all(feature = "prometheus_monitor", feature = "std"))]
14-
pub use prometheus::PrometheusMonitor;
15-
#[cfg(feature = "std")]
16-
pub mod disk;
17-
use alloc::{borrow::Cow, fmt::Debug, string::String, vec::Vec};
24+
use alloc::{
25+
borrow::Cow,
26+
fmt::Debug,
27+
string::{String, ToString},
28+
vec::Vec,
29+
};
1830
use core::{fmt, fmt::Write, time::Duration};
1931

20-
#[cfg(feature = "std")]
21-
pub use disk::{OnDiskJsonMonitor, OnDiskTomlMonitor};
2232
use hashbrown::HashMap;
2333
use libafl_bolts::{current_time, format_duration_hms, ClientId};
34+
#[cfg(all(feature = "prometheus_monitor", feature = "std"))]
35+
pub use prometheus::PrometheusMonitor;
2436
use serde::{Deserialize, Serialize};
2537

2638
#[cfg(feature = "afl_exec_sec")]

0 commit comments

Comments
 (0)