use alloc::string::String;
use core::fmt::{Debug, Formatter, Write};
use libafl_bolts::{ClientId, Error, current_time};
use crate::monitors::{Monitor, stats::ClientStatsManager};
#[derive(Clone)]
pub struct MultiMonitor<F>
where
F: FnMut(&str),
{
print_fn: F,
pizza_mode: bool,
}
impl<F> Debug for MultiMonitor<F>
where
F: FnMut(&str),
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MultiMonitor").finish_non_exhaustive()
}
}
impl<F> Monitor for MultiMonitor<F>
where
F: FnMut(&str),
{
fn display(
&mut self,
client_stats_manager: &mut ClientStatsManager,
event_msg: &str,
sender_id: ClientId,
) -> Result<(), Error> {
let sender = format!("#{}", sender_id.0);
let pad = if event_msg.len() + sender.len() < 13 {
" ".repeat(13 - event_msg.len() - sender.len())
} else {
String::new()
};
let head = format!("{event_msg}{pad} {sender}");
let global_stats = client_stats_manager.global_stats();
let mut global_fmt = if self.pizza_mode {
format!(
"[{}] (GLOBAL) time to bake: {}, customers: {}, pizzas: {}, deliveries: {}, doughs: {}, p/s: {}, Pineapple pizzas: 0",
head,
global_stats.run_time_pretty,
global_stats.client_stats_count,
global_stats.corpus_size,
global_stats.objective_size,
global_stats.total_execs,
global_stats.execs_per_sec_pretty
)
} else {
format!(
"[{}] (GLOBAL) run time: {}, clients: {}, corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
head,
global_stats.run_time_pretty,
global_stats.client_stats_count,
global_stats.corpus_size,
global_stats.objective_size,
global_stats.total_execs,
global_stats.execs_per_sec_pretty
)
};
for (key, val) in client_stats_manager.aggregated() {
write!(global_fmt, ", {key}: {val}").unwrap();
}
(self.print_fn)(&global_fmt);
client_stats_manager.client_stats_insert(sender_id)?;
let cur_time = current_time();
let exec_sec_val = client_stats_manager
.update_client_stats_for(sender_id, |client| client.execs_per_sec_pretty(cur_time))?;
let client = client_stats_manager.client_stats_for(sender_id)?;
let mut fmt = if self.pizza_mode {
format!(
" {} (CUSTOMER) pizzas: {}, deliveries: {}, doughs: {}, p/s: {}",
pad,
client.corpus_size(),
client.objective_size(),
client.executions(),
exec_sec_val
)
} else {
format!(
" {} (CLIENT) corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
pad,
client.corpus_size(),
client.objective_size(),
client.executions(),
exec_sec_val
)
};
for (key, val) in client.user_stats() {
write!(fmt, ", {key}: {val}").unwrap();
}
(self.print_fn)(&fmt);
#[cfg(feature = "introspection")]
{
let client_header = if self.pizza_mode {
"Customer"
} else {
"Client"
};
let cycles_label = if self.pizza_mode {
"pizzas baked"
} else {
"cycles"
};
for (client_id, client) in client_stats_manager
.client_stats()
.iter()
.filter(|(_, x)| x.enabled() && x.introspection_stats.elapsed_cycles() > 0)
{
let fmt = format!(
"{} {:03}:\n{} {}: {}",
client_header,
client_id.0,
client.introspection_stats,
cycles_label,
client.executions()
);
(self.print_fn)(&fmt);
}
(self.print_fn)("\n");
}
Ok(())
}
}
impl<F> MultiMonitor<F>
where
F: FnMut(&str),
{
pub fn new(print_fn: F) -> Self {
Self {
print_fn,
pizza_mode: crate::monitors::pizza_is_served(),
}
}
pub fn add_pineapple(&mut self) {
self.pizza_mode = false;
}
}