diff --git a/libafl/src/monitors/mod.rs b/libafl/src/monitors/mod.rs index 5a0d31a821..405cd28929 100644 --- a/libafl/src/monitors/mod.rs +++ b/libafl/src/monitors/mod.rs @@ -1302,3 +1302,79 @@ impl Default for ClientPerfMonitor { Self::new() } } + +/// A combined monitor consisting of multiple [`Monitor`]s +#[derive(Debug, Clone)] +pub struct CombinedMonitor { + first: A, + second: B, + start_time: Duration, + /// Client stats. This will be maintained to be consistent with + /// client stats of first and second monitor. + /// + /// Currently, the client stats will be synced to first and second + /// before each display call. + client_stats: Vec, +} + +impl CombinedMonitor { + /// Create a new combined monitor + pub fn new(mut first: A, mut second: B) -> Self { + let start_time = current_time(); + first.set_start_time(start_time); + second.set_start_time(start_time); + Self { + first, + second, + start_time, + client_stats: vec![], + } + } +} + +impl Monitor for CombinedMonitor { + fn client_stats_mut(&mut self) -> &mut Vec { + &mut self.client_stats + } + + fn client_stats(&self) -> &[ClientStats] { + &self.client_stats + } + + fn start_time(&self) -> Duration { + self.start_time + } + + fn set_start_time(&mut self, time: Duration) { + self.start_time = time; + self.first.set_start_time(time); + self.second.set_start_time(time); + } + + fn display(&mut self, event_msg: &str, sender_id: ClientId) { + self.first.client_stats_mut().clone_from(&self.client_stats); + self.first.display(event_msg, sender_id); + self.second + .client_stats_mut() + .clone_from(&self.client_stats); + self.second.display(event_msg, sender_id); + } + + fn aggregate(&mut self, name: &str) { + self.first.aggregate(name); + self.second.aggregate(name); + } +} + +/// Variadic macro to create a chain of [`Monitor`] +#[macro_export] +macro_rules! combine_monitor { + ( $last:expr ) => { $last }; + + ( $last:expr, ) => { $last }; + + ( $head:expr, $($tail:expr),+ $(,)?) => { + // recursive call + $crate::monitors::CombinedMonitor::new($head , $crate::combine_monitor!($($tail),+)) + }; +}