@@ -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+
153183fn 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.
191302struct 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