@@ -5,11 +5,12 @@ use anyhow::{Error, Result, bail};
55use kani_metadata:: { ArtifactType , HarnessKind , HarnessMetadata } ;
66use rayon:: prelude:: * ;
77use std:: fs:: File ;
8- use std:: io:: Write ;
8+ use std:: io:: { IsTerminal , Write } ;
99use std:: path:: Path ;
1010
1111use crate :: args:: { NumThreads , OutputFormat } ;
1212use crate :: call_cbmc:: { VerificationResult , VerificationStatus } ;
13+ use crate :: progress_indicator:: ProgressIndicator ;
1314use crate :: project:: Project ;
1415use crate :: session:: { BUG_REPORT_URL , KaniSession } ;
1516
@@ -56,6 +57,15 @@ impl<'pr> HarnessRunner<'_, 'pr> {
5657 harnesses : & ' pr [ & HarnessMetadata ] ,
5758 ) -> Result < Vec < HarnessResult < ' pr > > > {
5859 let sorted_harnesses = crate :: metadata:: sort_harnesses_by_loc ( harnesses) ;
60+
61+ // Determine if we should show progress indicator
62+ let show_progress = self . sess . args . log_file . is_some ( )
63+ && !self . sess . args . common_args . quiet
64+ && std:: io:: stdout ( ) . is_terminal ( ) ;
65+
66+ // Create progress indicator
67+ let progress_indicator = ProgressIndicator :: new ( sorted_harnesses. len ( ) , show_progress) ;
68+
5969 let pool = {
6070 let mut builder = rayon:: ThreadPoolBuilder :: new ( ) ;
6171 match self . sess . args . jobs ( ) {
@@ -86,6 +96,15 @@ impl<'pr> HarnessRunner<'_, 'pr> {
8696 }
8797
8898 let result = self . sess . check_harness ( goto_file, harness) ?;
99+
100+ // Update progress indicator if active
101+ if progress_indicator. is_active ( ) {
102+ let succeeded = result. status == VerificationStatus :: Success ;
103+ let timed_out =
104+ matches ! ( & result. results, Err ( crate :: call_cbmc:: ExitStatus :: Timeout ) ) ;
105+ progress_indicator. update_with_result ( succeeded, timed_out) ;
106+ }
107+
89108 if self . sess . args . fail_fast && result. status == VerificationStatus :: Failure {
90109 Err ( Error :: new ( FailFastHarnessInfo {
91110 index_to_failing_harness : idx,
@@ -97,6 +116,10 @@ impl<'pr> HarnessRunner<'_, 'pr> {
97116 } )
98117 . collect :: < Result < Vec < _ > > > ( )
99118 } ) ;
119+
120+ // Finish progress indicator
121+ progress_indicator. finish ( ) ;
122+
100123 match results {
101124 Ok ( results) => Ok ( results) ,
102125 Err ( err) => {
@@ -127,14 +150,40 @@ impl KaniSession {
127150 }
128151
129152 let output = result. render ( & self . args . output_format , harness. attributes . should_panic ) ;
130- if rayon:: current_num_threads ( ) > 1 {
131- println ! ( "Thread {thread_index}: {output}" ) ;
153+
154+ // If log file is specified, write to log file instead of stdout
155+ if let Some ( ref log_file_path) = self . args . log_file {
156+ self . write_to_log_file ( log_file_path, & output, thread_index) ;
132157 } else {
133- println ! ( "{output}" ) ;
158+ // Normal stdout output
159+ if rayon:: current_num_threads ( ) > 1 {
160+ println ! ( "Thread {thread_index}: {output}" ) ;
161+ } else {
162+ println ! ( "{output}" ) ;
163+ }
134164 }
135165 }
136166 }
137167
168+ fn write_to_log_file ( & self , log_file_path : & PathBuf , output : & str , thread_index : usize ) {
169+ use std:: fs:: OpenOptions ;
170+
171+ let result = OpenOptions :: new ( ) . create ( true ) . append ( true ) . open ( log_file_path) . and_then (
172+ |mut file| {
173+ let formatted_output = if rayon:: current_num_threads ( ) > 1 {
174+ format ! ( "Thread {thread_index}: {output}\n " )
175+ } else {
176+ format ! ( "{output}\n " )
177+ } ;
178+ file. write_all ( formatted_output. as_bytes ( ) )
179+ } ,
180+ ) ;
181+
182+ if let Err ( e) = result {
183+ eprintln ! ( "Failed to write to log file {}: {}" , log_file_path. display( ) , e) ;
184+ }
185+ }
186+
138187 fn should_print_output ( & self ) -> bool {
139188 !self . args . common_args . quiet && self . args . output_format != OutputFormat :: Old
140189 }
@@ -179,6 +228,10 @@ impl KaniSession {
179228 harness : & HarnessMetadata ,
180229 ) -> Result < VerificationResult > {
181230 let thread_index = rayon:: current_thread_index ( ) . unwrap_or_default ( ) ;
231+
232+ // Determine if we should suppress console output (when using log file with progress indicator)
233+ let suppress_console = self . args . log_file . is_some ( ) && std:: io:: stdout ( ) . is_terminal ( ) ;
234+
182235 if !self . args . common_args . quiet {
183236 // If the harness is automatically generated, pretty_name refers to the function under verification.
184237 let mut msg = if harness. is_automatically_generated {
@@ -201,7 +254,12 @@ impl KaniSession {
201254 msg = format ! ( "Thread {thread_index}: {msg}" ) ;
202255 }
203256
204- println ! ( "{msg}" ) ;
257+ // Write to log file if specified, otherwise to stdout
258+ if let Some ( ref log_file_path) = self . args . log_file {
259+ self . write_to_log_file ( log_file_path, & msg, thread_index) ;
260+ } else if !suppress_console {
261+ println ! ( "{msg}" ) ;
262+ }
205263 }
206264
207265 let mut result = self . with_timer ( || self . run_cbmc ( binary, harness) , "run_cbmc" ) ?;
0 commit comments