Skip to content

Commit dd1e8d1

Browse files
author
lukacan
committed
🔥 Improve progress bar
1 parent 39f65ea commit dd1e8d1

1 file changed

Lines changed: 137 additions & 10 deletions

File tree

crates/fuzz/src/trident/flow_executor.rs

Lines changed: 137 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,36 @@ fn paint_magenta(text: &str) -> String {
150150
}
151151
}
152152

153+
fn paint_green(text: &str) -> String {
154+
if colors_enabled() {
155+
format!("\x1b[32m{}\x1b[0m", text)
156+
} else {
157+
text.to_string()
158+
}
159+
}
160+
161+
fn paint_dim(text: &str) -> String {
162+
if colors_enabled() {
163+
format!("\x1b[2m{}\x1b[0m", text)
164+
} else {
165+
text.to_string()
166+
}
167+
}
168+
169+
fn paint_counter(value: u64, is_problem: bool) -> String {
170+
let raw = value.to_string();
171+
if !colors_enabled() {
172+
return raw;
173+
}
174+
if value == 0 {
175+
paint_green(&raw)
176+
} else if is_problem {
177+
paint_red(&raw)
178+
} else {
179+
paint_bold_yellow(&raw)
180+
}
181+
}
182+
153183
fn format_invariant_line(text: &str) -> String {
154184
const PREFIX: &str = "Assertion failed at ";
155185
const SEED_PREFIX: &str = " (seed: ";
@@ -187,6 +217,87 @@ fn format_invariant_line(text: &str) -> String {
187217
format!("{} {}", paint_red("!"), text)
188218
}
189219

220+
fn format_exec_rate(completed: u64, elapsed: std::time::Duration) -> f64 {
221+
let secs = elapsed.as_secs_f64();
222+
if secs <= f64::EPSILON {
223+
0.0
224+
} else {
225+
completed as f64 / secs
226+
}
227+
}
228+
229+
fn format_live_status_single(
230+
iteration: u64,
231+
iterations: u64,
232+
completed_flow_calls: u64,
233+
total_flow_calls: u64,
234+
invariant_failures: u64,
235+
program_panics: u64,
236+
elapsed: std::time::Duration,
237+
) -> String {
238+
let exec_rate = format_exec_rate(completed_flow_calls, elapsed);
239+
let iter_label = paint_cyan("iter");
240+
let flow_label = paint_cyan("flow calls");
241+
let inv_label = paint_magenta("inv");
242+
let panics_label = paint_magenta("panics");
243+
let exec_label = paint_cyan("exec/s");
244+
let inv_val = paint_counter(invariant_failures, true);
245+
let panic_val = paint_counter(program_panics, true);
246+
let iter_sep = paint_dim("|");
247+
let metric_sep = paint_dim("|");
248+
format!(
249+
"{iter_label}: {iteration}/{iterations} {iter_sep} {flow_label}: {completed_flow_calls}/{total_flow_calls}\n{inv_label}: {inv_val} {metric_sep} {panics_label}: {panic_val} {metric_sep} {exec_label}: {exec_rate:.0}",
250+
iter_label = iter_label,
251+
iteration = iteration,
252+
iterations = iterations,
253+
iter_sep = iter_sep,
254+
flow_label = flow_label,
255+
completed_flow_calls = completed_flow_calls,
256+
total_flow_calls = total_flow_calls,
257+
inv_label = inv_label,
258+
inv_val = inv_val,
259+
metric_sep = metric_sep,
260+
panics_label = panics_label,
261+
panic_val = panic_val,
262+
exec_label = exec_label,
263+
exec_rate = exec_rate
264+
)
265+
}
266+
267+
fn format_live_status_parallel(
268+
num_threads: usize,
269+
completed_flow_calls: u64,
270+
total_flow_calls: u64,
271+
invariant_failures: u64,
272+
program_panics: u64,
273+
elapsed: std::time::Duration,
274+
) -> String {
275+
let exec_rate = format_exec_rate(completed_flow_calls, elapsed);
276+
let threads_label = paint_cyan("threads");
277+
let flow_label = paint_cyan("flow calls");
278+
let inv_label = paint_magenta("inv");
279+
let panics_label = paint_magenta("panics");
280+
let exec_label = paint_cyan("exec/s");
281+
let inv_val = paint_counter(invariant_failures, true);
282+
let panic_val = paint_counter(program_panics, true);
283+
let sep = paint_dim("|");
284+
format!(
285+
"{threads_label}: {num_threads} {sep} {flow_label}: {completed_flow_calls}/{total_flow_calls}\n{inv_label}: {inv_val} {sep} {panics_label}: {panic_val} {sep} {exec_label}: {exec_rate:.0}",
286+
threads_label = threads_label,
287+
num_threads = num_threads,
288+
sep = sep,
289+
flow_label = flow_label,
290+
completed_flow_calls = completed_flow_calls,
291+
total_flow_calls = total_flow_calls,
292+
inv_label = inv_label,
293+
inv_val = inv_val,
294+
panics_label = panics_label,
295+
panic_val = panic_val,
296+
exec_label = exec_label,
297+
exec_rate = exec_rate
298+
)
299+
}
300+
190301
/// Final aggregated runtime summary produced by the UI/controller thread.
191302
struct ParallelRunSummary {
192303
invariant_failures: u64,
@@ -406,6 +517,9 @@ pub trait FlowExecutor: Send + 'static + Sized {
406517
let mut invariant_failed = false; // Tracks fuzz test assertion/invariant failures
407518
let mut invariant_failure_count: u64 = 0;
408519
let mut panic_messages: Vec<String> = Vec::new();
520+
let total_flow_calls = iterations * flow_calls_per_iteration;
521+
let mut completed_flow_calls = 0u64;
522+
let start_time = Instant::now();
409523

410524
// Configure debug seed if in debug mode
411525
if is_debug_mode {
@@ -419,7 +533,6 @@ pub trait FlowExecutor: Send + 'static + Sized {
419533
let pb = if is_debug_mode {
420534
None
421535
} else {
422-
let total_flow_calls = iterations * flow_calls_per_iteration;
423536
let pb = indicatif::ProgressBar::new(total_flow_calls);
424537
pb.set_style(
425538
indicatif::ProgressStyle::with_template(
@@ -475,16 +588,19 @@ pub trait FlowExecutor: Send + 'static + Sized {
475588
// Update progress bar with live stats
476589
if let Some(ref pb) = pb {
477590
pb.inc(flow_calls_per_iteration);
591+
completed_flow_calls += flow_calls_per_iteration;
478592
let program_panics = fuzzer
479593
.trident_mut()
480594
.get_fuzzing_data()
481595
.get_program_panic_count();
482-
pb.set_message(format!(
483-
"Iteration {}/{} | Invariant failures: {} | Program panics: {}",
596+
pb.set_message(format_live_status_single(
484597
i + 1,
485598
iterations,
599+
completed_flow_calls,
600+
total_flow_calls,
486601
invariant_failure_count,
487-
program_panics
602+
program_panics,
603+
start_time.elapsed(),
488604
));
489605
}
490606
}
@@ -547,9 +663,14 @@ pub trait FlowExecutor: Send + 'static + Sized {
547663
.unwrap()
548664
.progress_chars("#>-"),
549665
);
550-
main_pb.set_message(format!(
551-
"Fuzzing with {} threads | Invariant failures: 0 | Program panics: 0",
552-
num_threads
666+
let ui_start_time = Instant::now();
667+
main_pb.set_message(format_live_status_parallel(
668+
num_threads,
669+
0,
670+
total_flow_calls,
671+
0,
672+
0,
673+
ui_start_time.elapsed(),
553674
));
554675

555676
// Single UI/controller owner: consumes worker events, updates progress bar,
@@ -558,10 +679,12 @@ pub trait FlowExecutor: Send + 'static + Sized {
558679
let mut invariant_failures = 0u64;
559680
let mut program_panics = 0u64;
560681
let mut panic_messages = Vec::new();
682+
let mut completed_flow_calls = 0u64;
561683

562684
while let Ok(event) = event_rx.recv() {
563685
match event {
564686
WorkerEvent::ProgressDelta(delta) => {
687+
completed_flow_calls += delta;
565688
main_pb.inc(delta);
566689
}
567690
WorkerEvent::InvariantFailure(message) => {
@@ -573,9 +696,13 @@ pub trait FlowExecutor: Send + 'static + Sized {
573696
}
574697
}
575698

576-
main_pb.set_message(format!(
577-
"Invariant failures: {} | Program panics: {}",
578-
invariant_failures, program_panics
699+
main_pb.set_message(format_live_status_parallel(
700+
num_threads,
701+
completed_flow_calls,
702+
total_flow_calls,
703+
invariant_failures,
704+
program_panics,
705+
ui_start_time.elapsed(),
579706
));
580707
}
581708

0 commit comments

Comments
 (0)