Add total execs to TUI (#3078)

* Add total execs to TUI

* Pretty print large numbers

---------

Co-authored-by: Your Name <you@example.com>
This commit is contained in:
WorksButNotTested 2025-03-14 19:47:51 +00:00 committed by GitHub
parent 8e32947db6
commit d317725170
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 17 deletions

View File

@ -170,9 +170,10 @@ impl ClientStatsManager {
/// Get process timing. `execs_per_sec_pretty` could be retrieved from `GlobalStats`. /// Get process timing. `execs_per_sec_pretty` could be retrieved from `GlobalStats`.
#[must_use] #[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(); let mut total_process_timing = ProcessTiming::new();
total_process_timing.exec_speed = execs_per_sec_pretty; total_process_timing.exec_speed = execs_per_sec_pretty;
total_process_timing.total_execs = total_execs;
if self.client_stats().len() > 1 { if self.client_stats().len() > 1 {
let mut new_path_time = Duration::default(); let mut new_path_time = Duration::default();
let mut new_objectives_time = Duration::default(); let mut new_objectives_time = Duration::default();

View File

@ -94,6 +94,8 @@ pub struct ProcessTiming {
pub last_new_entry: Duration, pub last_new_entry: Duration,
/// Timing of the last new solution /// Timing of the last new solution
pub last_saved_solution: Duration, pub last_saved_solution: Duration,
/// The total number of executions
pub total_execs: u64,
} }
impl ProcessTiming { impl ProcessTiming {
@ -339,12 +341,14 @@ impl ClientStats {
}; };
let exec_speed = self.execs_per_sec_pretty(current_time()); let exec_speed = self.execs_per_sec_pretty(current_time());
let total_execs = self.executions;
ProcessTiming { ProcessTiming {
client_start_time, client_start_time,
exec_speed, exec_speed,
last_new_entry, last_new_entry,
last_saved_solution, last_saved_solution,
total_execs,
} }
} }

View File

@ -26,7 +26,7 @@ use crossterm::{
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
}; };
use hashbrown::HashMap; 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 ratatui::{Terminal, backend::CrosstermBackend};
use typed_builder::TypedBuilder; use typed_builder::TypedBuilder;
@ -351,6 +351,7 @@ impl Monitor for TuiMonitor {
let totalexec = global_stats.total_execs; let totalexec = global_stats.total_execs;
let run_time = global_stats.run_time; let run_time = global_stats.run_time;
let exec_per_sec_pretty = global_stats.execs_per_sec_pretty.clone(); 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(); let mut ctx = self.context.write().unwrap();
ctx.total_corpus_count = global_stats.corpus_size; ctx.total_corpus_count = global_stats.corpus_size;
ctx.total_solutions = global_stats.objective_size; ctx.total_solutions = global_stats.objective_size;
@ -358,7 +359,8 @@ impl Monitor for TuiMonitor {
.add(run_time, global_stats.corpus_size); .add(run_time, global_stats.corpus_size);
ctx.objective_size_timed ctx.objective_size_timed
.add(run_time, global_stats.objective_size); .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.total_process_timing = total_process_timing;
ctx.execs_per_sec_timed.add(run_time, execsec); ctx.execs_per_sec_timed.add(run_time, execsec);
@ -391,9 +393,9 @@ impl Monitor for TuiMonitor {
let mut fmt = format!( let mut fmt = format!(
"[{}] corpus: {}, objectives: {}, executions: {}, exec/sec: {}", "[{}] corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
head, head,
client.corpus_size(), format_big_number(client.corpus_size()),
client.objective_size(), format_big_number(client.objective_size()),
client.executions(), format_big_number(client.executions()),
exec_sec exec_sec
); );
for (key, val) in client.user_stats() { for (key, val) in client.user_stats() {

View File

@ -3,6 +3,7 @@ use alloc::{string::ToString, sync::Arc, vec::Vec};
use core::cmp::{max, min}; use core::cmp::{max, min};
use std::sync::RwLock; use std::sync::RwLock;
use libafl_bolts::format_big_number;
use ratatui::{ use ratatui::{
Frame, Frame,
layout::{Alignment, Constraint, Direction, Layout, Rect}, layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -532,6 +533,10 @@ impl TuiUi {
Cell::from(Span::raw("exec speed")), Cell::from(Span::raw("exec speed")),
Cell::from(Span::raw(&tup.1.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![ Row::new(vec![
Cell::from(Span::raw("last new entry")), Cell::from(Span::raw("last new entry")),
Cell::from(Span::raw(format_duration_hms(&(tup.1.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("clients")),
Cell::from(Span::raw(format!("{}", self.clients))), Cell::from(Span::raw(format!("{}", self.clients))),
Cell::from(Span::raw("total execs")), 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("map density")),
Cell::from(Span::raw(app.total_map_density.to_string())), Cell::from(Span::raw(app.total_map_density.to_string())),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Span::raw("solutions")), 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("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("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![ vec![
Row::new(vec![ Row::new(vec![
Cell::from(Span::raw("corpus count")), Cell::from(Span::raw("corpus count")),
Cell::from(Span::raw(format!( Cell::from(Span::raw(format_big_number(
"{}", app.clients.get(&self.clients_idx).map_or(0, |x| x.corpus),
app.clients.get(&self.clients_idx).map_or(0, |x| x.corpus)
))), ))),
]), ]),
Row::new(vec![ Row::new(vec![
Cell::from(Span::raw("total execs")), Cell::from(Span::raw("total execs")),
Cell::from(Span::raw(format!( Cell::from(Span::raw(format_big_number(
"{}",
app.clients app.clients
.get(&self.clients_idx) .get(&self.clients_idx)
.map_or(0, |x| x.executions) .map_or(0, |x| x.executions),
))), ))),
]), ]),
Row::new(vec![ Row::new(vec![

View File

@ -140,7 +140,7 @@ pub mod bolts_prelude {
#[cfg(all(unix, feature = "std"))] #[cfg(all(unix, feature = "std"))]
use alloc::boxed::Box; use alloc::boxed::Box;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::{borrow::Cow, vec::Vec}; use alloc::{borrow::Cow, string::ToString, vec::Vec};
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))] #[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
use core::hash::BuildHasher; use core::hash::BuildHasher;
#[cfg(any(feature = "xxh3", feature = "alloc"))] #[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!("{}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::<String>();
format!("{short} ({long})")
}
/// Stderr logger /// Stderr logger
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new(); pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new();