@@ -22,6 +22,17 @@ use simics::{
2222 debug, get_processor_number, info, trace, warn,
2323} ;
2424
25+ enum IterationControl {
26+ Continue ,
27+ StopRequested ,
28+ }
29+
30+ enum IterationCount {
31+ NoCount ,
32+ Timeout ,
33+ Solution ,
34+ }
35+
2536impl Tsffs {
2637 fn on_simulation_stopped_magic_start ( & mut self , magic_number : MagicNumber ) -> Result < ( ) > {
2738 if !self . have_initial_snapshot ( ) {
@@ -91,6 +102,80 @@ impl Tsffs {
91102 self . on_simulation_stopped_solution ( SolutionKind :: Manual )
92103 }
93104
105+ fn finish_iteration (
106+ & mut self ,
107+ exit_kind : ExitKind ,
108+ iteration_count : IterationCount ,
109+ missing_start_info_message : & str ,
110+ ) -> Result < IterationControl > {
111+ // 1) Count this iteration as complete.
112+ self . iterations += 1 ;
113+
114+ // 2) Enforce iteration cap before scheduling/resuming work for next iteration.
115+ if self . iteration_limit != 0 && self . iterations >= self . iteration_limit {
116+ let duration = SystemTime :: now ( ) . duration_since (
117+ * self
118+ . start_time
119+ . get ( )
120+ . ok_or_else ( || anyhow ! ( "Start time was not set" ) ) ?,
121+ ) ?;
122+
123+ // Set the log level so this message always prints
124+ set_log_level ( self . as_conf_object_mut ( ) , LogLevel :: Info ) ?;
125+
126+ info ! (
127+ self . as_conf_object( ) ,
128+ "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s)." ,
129+ self . iterations,
130+ duration. as_secs_f32( ) ,
131+ self . iterations as f32 / duration. as_secs_f32( )
132+ ) ;
133+
134+ self . send_shutdown ( ) ?;
135+
136+ if self . quit_on_iteration_limit {
137+ quit ( 0 ) ?;
138+ } else {
139+ return Ok ( IterationControl :: StopRequested ) ;
140+ }
141+ }
142+
143+ // 3) Update outcome counters where this stop reason contributes to stats.
144+ match iteration_count {
145+ IterationCount :: NoCount => { }
146+ IterationCount :: Timeout => self . timeouts += 1 ,
147+ IterationCount :: Solution => self . solutions += 1 ,
148+ }
149+
150+ let fuzzer_tx = self
151+ . fuzzer_tx
152+ . get ( )
153+ . ok_or_else ( || anyhow ! ( "No fuzzer tx channel" ) ) ?;
154+
155+ // 4) Publish this iteration result back to the fuzzer loop.
156+ fuzzer_tx. send ( exit_kind) ?;
157+
158+ // 5) Restore to initial snapshot according to the configured restore interval.
159+ if self . should_restore_snapshot_this_iteration ( ) {
160+ self . restore_initial_snapshot ( ) ?;
161+ }
162+
163+ // 6) Reset AFL edge chaining state for the next execution.
164+ self . coverage_prev_loc = 0 ;
165+
166+ // 7) Persist testcase bytes when start metadata is available.
167+ if self . start_info . get ( ) . is_some ( ) {
168+ self . get_and_write_testcase ( ) ?;
169+ } else {
170+ debug ! ( self . as_conf_object( ) , "{missing_start_info_message}" ) ;
171+ }
172+
173+ // 8) Arm timeout for the next iteration run.
174+ self . post_timeout_event ( ) ?;
175+
176+ Ok ( IterationControl :: Continue )
177+ }
178+
94179 fn on_simulation_stopped_magic_stop ( & mut self ) -> Result < ( ) > {
95180 if !self . have_initial_snapshot ( ) {
96181 warn ! (
@@ -117,56 +202,14 @@ impl Tsffs {
117202 return Ok ( ( ) ) ;
118203 }
119204
120- self . iterations += 1 ;
121-
122- if self . iteration_limit != 0 && self . iterations >= self . iteration_limit {
123- let duration = SystemTime :: now ( ) . duration_since (
124- * self
125- . start_time
126- . get ( )
127- . ok_or_else ( || anyhow ! ( "Start time was not set" ) ) ?,
128- ) ?;
129-
130- // Set the log level so this message always prints
131- set_log_level ( self . as_conf_object_mut ( ) , LogLevel :: Info ) ?;
132-
133- info ! (
134- self . as_conf_object( ) ,
135- "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s)." ,
136- self . iterations,
137- duration. as_secs_f32( ) ,
138- self . iterations as f32 / duration. as_secs_f32( )
139- ) ;
140-
141- self . send_shutdown ( ) ?;
142-
143- if self . quit_on_iteration_limit {
144- quit ( 0 ) ?;
145- } else {
146- return Ok ( ( ) ) ;
147- }
148- }
149-
150- let fuzzer_tx = self
151- . fuzzer_tx
152- . get ( )
153- . ok_or_else ( || anyhow ! ( "No fuzzer tx channel" ) ) ?;
154-
155- fuzzer_tx. send ( ExitKind :: Ok ) ?;
156-
157- self . restore_initial_snapshot ( ) ?;
158- self . coverage_prev_loc = 0 ;
159-
160- if self . start_info . get ( ) . is_some ( ) {
161- self . get_and_write_testcase ( ) ?;
162- } else {
163- debug ! (
164- self . as_conf_object( ) ,
165- "Missing start buffer or size, not writing testcase."
166- ) ;
205+ // Normal stop path: report successful completion without solution/timeout counters.
206+ if let IterationControl :: StopRequested = self . finish_iteration (
207+ ExitKind :: Ok ,
208+ IterationCount :: NoCount ,
209+ "Missing start buffer or size, not writing testcase." ,
210+ ) ? {
211+ return Ok ( ( ) ) ;
167212 }
168-
169- self . post_timeout_event ( ) ?;
170213 }
171214
172215 if self . save_all_execution_traces {
@@ -328,56 +371,14 @@ impl Tsffs {
328371 return Ok ( ( ) ) ;
329372 }
330373
331- self . iterations += 1 ;
332-
333- if self . iteration_limit != 0 && self . iterations >= self . iteration_limit {
334- let duration = SystemTime :: now ( ) . duration_since (
335- * self
336- . start_time
337- . get ( )
338- . ok_or_else ( || anyhow ! ( "Start time was not set" ) ) ?,
339- ) ?;
340-
341- // Set the log level so this message always prints
342- set_log_level ( self . as_conf_object_mut ( ) , LogLevel :: Info ) ?;
343-
344- info ! (
345- self . as_conf_object( ) ,
346- "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s)." ,
347- self . iterations,
348- duration. as_secs_f32( ) ,
349- self . iterations as f32 / duration. as_secs_f32( )
350- ) ;
351-
352- self . send_shutdown ( ) ?;
353-
354- if self . quit_on_iteration_limit {
355- quit ( 0 ) ?;
356- } else {
357- return Ok ( ( ) ) ;
358- }
359- }
360-
361- let fuzzer_tx = self
362- . fuzzer_tx
363- . get ( )
364- . ok_or_else ( || anyhow ! ( "No fuzzer tx channel" ) ) ?;
365-
366- fuzzer_tx. send ( ExitKind :: Ok ) ?;
367-
368- self . restore_initial_snapshot ( ) ?;
369- self . coverage_prev_loc = 0 ;
370-
371- if self . start_info . get ( ) . is_some ( ) {
372- self . get_and_write_testcase ( ) ?;
373- } else {
374- debug ! (
375- self . as_conf_object( ) ,
376- "Missing start buffer or size, not writing testcase. This may be due to using manual no-buffer harnessing."
377- ) ;
374+ // Manual stop behaves like normal completion for accounting purposes.
375+ if let IterationControl :: StopRequested = self . finish_iteration (
376+ ExitKind :: Ok ,
377+ IterationCount :: NoCount ,
378+ "Missing start buffer or size, not writing testcase. This may be due to using manual no-buffer harnessing." ,
379+ ) ? {
380+ return Ok ( ( ) ) ;
378381 }
379-
380- self . post_timeout_event ( ) ?;
381382 }
382383
383384 if self . save_all_execution_traces {
@@ -424,65 +425,21 @@ impl Tsffs {
424425 return Ok ( ( ) ) ;
425426 }
426427
427- self . iterations += 1 ;
428-
429- if self . iteration_limit != 0 && self . iterations >= self . iteration_limit {
430- let duration = SystemTime :: now ( ) . duration_since (
431- * self
432- . start_time
433- . get ( )
434- . ok_or_else ( || anyhow ! ( "Start time was not set" ) ) ?,
435- ) ?;
436-
437- // Set the log level so this message always prints
438- set_log_level ( self . as_conf_object_mut ( ) , LogLevel :: Info ) ?;
439-
440- info ! (
441- self . as_conf_object( ) ,
442- "Configured iteration count {} reached. Stopping after {} seconds ({} exec/s)." ,
443- self . iterations,
444- duration. as_secs_f32( ) ,
445- self . iterations as f32 / duration. as_secs_f32( )
446- ) ;
447-
448- self . send_shutdown ( ) ?;
449-
450- if self . quit_on_iteration_limit {
451- quit ( 0 ) ?;
452- } else {
453- return Ok ( ( ) ) ;
454- }
455- }
456-
457- let fuzzer_tx = self
458- . fuzzer_tx
459- . get ( )
460- . ok_or_else ( || anyhow ! ( "No fuzzer tx channel" ) ) ?;
461-
462- match kind {
463- SolutionKind :: Timeout => {
464- self . timeouts += 1 ;
465- fuzzer_tx. send ( ExitKind :: Timeout ) ?
466- }
428+ let ( exit_kind, iteration_count) = match kind {
429+ SolutionKind :: Timeout => ( ExitKind :: Timeout , IterationCount :: Timeout ) ,
467430 SolutionKind :: Exception | SolutionKind :: Breakpoint | SolutionKind :: Manual => {
468- self . solutions += 1 ;
469- fuzzer_tx. send ( ExitKind :: Crash ) ?
431+ ( ExitKind :: Crash , IterationCount :: Solution )
470432 }
471- }
472-
473- self . restore_initial_snapshot ( ) ?;
474- self . coverage_prev_loc = 0 ;
433+ } ;
475434
476- if self . start_info . get ( ) . is_some ( ) {
477- self . get_and_write_testcase ( ) ? ;
478- } else {
479- debug ! (
480- self . as_conf_object ( ) ,
481- "Missing start buffer or size, not writing testcase."
482- ) ;
435+ // Solution/timeout path: classify exit kind and increment corresponding counters.
436+ if let IterationControl :: StopRequested = self . finish_iteration (
437+ exit_kind ,
438+ iteration_count ,
439+ "Missing start buffer or size, not writing testcase." ,
440+ ) ? {
441+ return Ok ( ( ) ) ;
483442 }
484-
485- self . post_timeout_event ( ) ?;
486443 }
487444
488445 if self . save_all_execution_traces {
0 commit comments