diff --git a/libafl/src/monitors/stats/manager.rs b/libafl/src/monitors/stats/manager.rs index 4ea640c525..1538d13663 100644 --- a/libafl/src/monitors/stats/manager.rs +++ b/libafl/src/monitors/stats/manager.rs @@ -170,9 +170,10 @@ impl ClientStatsManager { /// Get process timing. `execs_per_sec_pretty` could be retrieved from `GlobalStats`. #[must_use] - pub fn process_timing(&self, execs_per_sec_pretty: String) -> ProcessTiming { + pub fn process_timing(&self, execs_per_sec_pretty: String, total_execs: u64) -> ProcessTiming { let mut total_process_timing = ProcessTiming::new(); total_process_timing.exec_speed = execs_per_sec_pretty; + total_process_timing.total_execs = total_execs; if self.client_stats().len() > 1 { let mut new_path_time = Duration::default(); let mut new_objectives_time = Duration::default(); diff --git a/libafl/src/monitors/stats/mod.rs b/libafl/src/monitors/stats/mod.rs index 979d3fedc5..11f77bf5b4 100644 --- a/libafl/src/monitors/stats/mod.rs +++ b/libafl/src/monitors/stats/mod.rs @@ -94,6 +94,8 @@ pub struct ProcessTiming { pub last_new_entry: Duration, /// Timing of the last new solution pub last_saved_solution: Duration, + /// The total number of executions + pub total_execs: u64, } impl ProcessTiming { @@ -339,12 +341,14 @@ impl ClientStats { }; let exec_speed = self.execs_per_sec_pretty(current_time()); + let total_execs = self.executions; ProcessTiming { client_start_time, exec_speed, last_new_entry, last_saved_solution, + total_execs, } } diff --git a/libafl/src/monitors/tui/mod.rs b/libafl/src/monitors/tui/mod.rs index ea51b6b28d..927dce874f 100644 --- a/libafl/src/monitors/tui/mod.rs +++ b/libafl/src/monitors/tui/mod.rs @@ -26,7 +26,7 @@ use crossterm::{ terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, }; use hashbrown::HashMap; -use libafl_bolts::{ClientId, current_time, format_duration_hms}; +use libafl_bolts::{ClientId, current_time, format_big_number, format_duration_hms}; use ratatui::{Terminal, backend::CrosstermBackend}; use typed_builder::TypedBuilder; @@ -351,6 +351,7 @@ impl Monitor for TuiMonitor { let totalexec = global_stats.total_execs; let run_time = global_stats.run_time; let exec_per_sec_pretty = global_stats.execs_per_sec_pretty.clone(); + let total_execs = global_stats.total_execs; let mut ctx = self.context.write().unwrap(); ctx.total_corpus_count = global_stats.corpus_size; ctx.total_solutions = global_stats.objective_size; @@ -358,7 +359,8 @@ impl Monitor for TuiMonitor { .add(run_time, global_stats.corpus_size); ctx.objective_size_timed .add(run_time, global_stats.objective_size); - let total_process_timing = client_stats_manager.process_timing(exec_per_sec_pretty); + let total_process_timing = + client_stats_manager.process_timing(exec_per_sec_pretty, total_execs); ctx.total_process_timing = total_process_timing; ctx.execs_per_sec_timed.add(run_time, execsec); @@ -391,9 +393,9 @@ impl Monitor for TuiMonitor { let mut fmt = format!( "[{}] corpus: {}, objectives: {}, executions: {}, exec/sec: {}", head, - client.corpus_size(), - client.objective_size(), - client.executions(), + format_big_number(client.corpus_size()), + format_big_number(client.objective_size()), + format_big_number(client.executions()), exec_sec ); for (key, val) in client.user_stats() { diff --git a/libafl/src/monitors/tui/ui.rs b/libafl/src/monitors/tui/ui.rs index 7287d61d17..66b4f11960 100644 --- a/libafl/src/monitors/tui/ui.rs +++ b/libafl/src/monitors/tui/ui.rs @@ -3,6 +3,7 @@ use alloc::{string::ToString, sync::Arc, vec::Vec}; use core::cmp::{max, min}; use std::sync::RwLock; +use libafl_bolts::format_big_number; use ratatui::{ Frame, layout::{Alignment, Constraint, Direction, Layout, Rect}, @@ -532,6 +533,10 @@ impl TuiUi { Cell::from(Span::raw("exec speed")), Cell::from(Span::raw(&tup.1.exec_speed)), ]), + Row::new(vec![ + Cell::from(Span::raw("total execs")), + Cell::from(Span::raw(format_big_number(tup.1.total_execs))), + ]), Row::new(vec![ Cell::from(Span::raw("last new entry")), Cell::from(Span::raw(format_duration_hms(&(tup.1.last_new_entry)))), @@ -581,17 +586,17 @@ impl TuiUi { Cell::from(Span::raw("clients")), Cell::from(Span::raw(format!("{}", self.clients))), Cell::from(Span::raw("total execs")), - Cell::from(Span::raw(format!("{}", app.total_execs))), + Cell::from(Span::raw(format_big_number(app.total_execs))), Cell::from(Span::raw("map density")), Cell::from(Span::raw(app.total_map_density.to_string())), ]), Row::new(vec![ Cell::from(Span::raw("solutions")), - Cell::from(Span::raw(format!("{}", app.total_solutions))), + Cell::from(Span::raw(format_big_number(app.total_solutions))), Cell::from(Span::raw("cycle done")), - Cell::from(Span::raw(format!("{}", app.total_cycles_done))), + Cell::from(Span::raw(format_big_number(app.total_cycles_done))), Cell::from(Span::raw("corpus count")), - Cell::from(Span::raw(format!("{}", app.total_corpus_count))), + Cell::from(Span::raw(format_big_number(app.total_corpus_count))), ]), ] }; @@ -680,18 +685,16 @@ impl TuiUi { vec![ Row::new(vec![ Cell::from(Span::raw("corpus count")), - Cell::from(Span::raw(format!( - "{}", - app.clients.get(&self.clients_idx).map_or(0, |x| x.corpus) + Cell::from(Span::raw(format_big_number( + app.clients.get(&self.clients_idx).map_or(0, |x| x.corpus), ))), ]), Row::new(vec![ Cell::from(Span::raw("total execs")), - Cell::from(Span::raw(format!( - "{}", + Cell::from(Span::raw(format_big_number( app.clients .get(&self.clients_idx) - .map_or(0, |x| x.executions) + .map_or(0, |x| x.executions), ))), ]), Row::new(vec![ diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index 85db227e29..a72e605190 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -140,7 +140,7 @@ pub mod bolts_prelude { #[cfg(all(unix, feature = "std"))] use alloc::boxed::Box; #[cfg(feature = "alloc")] -use alloc::{borrow::Cow, vec::Vec}; +use alloc::{borrow::Cow, string::ToString, vec::Vec}; #[cfg(all(not(feature = "xxh3"), feature = "alloc"))] use core::hash::BuildHasher; #[cfg(any(feature = "xxh3", feature = "alloc"))] @@ -945,6 +945,45 @@ pub fn format_duration_hms(duration: &time::Duration) -> String { format!("{}h-{}m-{}s", (secs / 60) / 60, (secs / 60) % 60, secs % 60) } +/// Format a number with thousands separators +#[cfg(feature = "alloc")] +#[must_use] +pub fn format_big_number(val: u64) -> String { + let short = { + let (num, unit) = match val { + 0..=999 => return format!("{val}"), + 1_000..=999_999 => (1000, "K"), + 1_000_000..=999_999_999 => (1_000_000, "M"), + 1_000_000_000..=999_999_999_999 => (1_000_000_000, "G"), + _ => (1_000_000_000_000, "T"), + }; + let main = val / num; + let frac = (val % num) / (num / 100); + format!( + "{}.{}{}", + main, + format!("{frac:02}").trim_end_matches('0'), + unit + ) + }; + let long = val + .to_string() + .chars() + .rev() + .enumerate() + .fold(String::new(), |mut acc, (i, c)| { + if i > 0 && i % 3 == 0 { + acc.push(','); + } + acc.push(c); + acc + }) + .chars() + .rev() + .collect::(); + format!("{short} ({long})") +} + /// Stderr logger #[cfg(feature = "std")] pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new();