3535 ZeroExt ,
3636 eq ,
3737 set_option ,
38+ unknown ,
3839 unsat ,
3940)
4041
107108 InvariantTestingContext ,
108109 PathContext ,
109110 SolverOutput ,
111+ dirname ,
110112 solve_end_to_end ,
111113 solve_low_level ,
112114)
126128 Word ,
127129 address ,
128130 color_error ,
131+ color_good ,
132+ color_info ,
133+ color_warn ,
129134 con ,
130135 con_addr ,
131136 create_solver ,
132- cyan ,
133137 extract_bytes ,
134- green ,
135138 hexify ,
136139 indent_text ,
137140 int_of ,
138- red ,
139141 smt_and ,
140142 smt_or ,
141143 uid ,
142144 unbox_int ,
143- yellow ,
144145)
145146
146147BAD_FILE_DESCRIPTOR = 9
@@ -646,11 +647,11 @@ def _compute_frontier(ctx: ContractContext, depth: int) -> Iterator[Exec]:
646647 for idx , pre_ex in enumerate (curr_exs ):
647648 ui .update_status (
648649 f"{ contract_name } : "
649- f"depth: { cyan (depth )} | "
650- f"starting states: { cyan (len (curr_exs ))} | "
651- f"unique states: { cyan (len (visited ))} | "
652- f"frontier states: { cyan (len (next_exs ))} | "
653- f"completed paths: { cyan (idx )} "
650+ f"depth: { color_info (depth )} | "
651+ f"starting states: { color_info (len (curr_exs ))} | "
652+ f"unique states: { color_info (len (visited ))} | "
653+ f"frontier states: { color_info (len (next_exs ))} | "
654+ f"completed paths: { color_info (idx )} "
654655 )
655656
656657 for addr in resolve_target_contracts (ctx .inv_ctx , pre_ex ):
@@ -908,6 +909,7 @@ def _solve_end_to_end_callback(
908909 raise
909910
910911 result , model = solver_output .result , solver_output .model
912+ path_id = solver_output .path_id
911913
912914 # keep track of the solver outputs, so that we can display PASS/FAIL/TIMEOUT/ERROR later
913915 ctx .solver_outputs .append (solver_output )
@@ -918,11 +920,17 @@ def _solve_end_to_end_callback(
918920 return
919921
920922 if result == "err" :
923+ self ._save_failed_query (path_id , solver_output , "error" )
921924 error (
922925 f"solver error: { solver_output .error } (returncode={ solver_output .returncode } )"
923926 )
924927 return
925928
929+ # handle "unknown" (timeout) result
930+ if result == unknown :
931+ self ._save_failed_query (path_id , solver_output , "timeout" )
932+ return
933+
926934 # model could be an empty dict here, so compare to None explicitly
927935 if model is None :
928936 return
@@ -936,14 +944,13 @@ def _solve_end_to_end_callback(
936944 if description :
937945 print (description )
938946
939- path_id = solver_output .path_id
940947 if args .verbose >= VERBOSITY_TRACE_COUNTEREXAMPLE :
941948 pid_str = f" #{ path_id } " if args .verbose >= VERBOSITY_TRACE_PATHS else ""
942949 print (f"Trace{ pid_str } :" )
943950 print (ctx .traces [path_id ], end = "" )
944951
945952 if model .is_valid :
946- print (red (f"Counterexample: { model } " ))
953+ print (color_error (f"Counterexample: { model } " ))
947954 ctx .valid_counterexamples .append (model )
948955
949956 # add the stacks from the temporary flamegraph to the global one
@@ -966,6 +973,33 @@ def _solve_end_to_end_callback(
966973 if sequence := ctx .call_sequences [path_id ]:
967974 print (f"Sequence:\n { sequence } " )
968975
976+ def _save_failed_query (
977+ self , path_id : int , solver_output : SolverOutput , failure_type : str
978+ ) -> None :
979+ """Save query and call sequence to a separate directory for debugging."""
980+ ctx = self .ctx
981+ args = ctx .args
982+
983+ debug_dir = f"{ dirname (ctx .solving_ctx .dump_dir )} -{ failure_type } "
984+ os .makedirs (debug_dir , exist_ok = True )
985+ query_file = solver_output .query_file
986+ debug_query_file = os .path .join (debug_dir , os .path .basename (query_file ))
987+ try :
988+ shutil .copy2 (query_file , debug_query_file )
989+ except Exception as e :
990+ error (f"Could not copy failed query to { failure_type } directory: { e } " )
991+
992+ # save call sequence to a separate file for debugging context
993+ if call_sequence := ctx .call_sequences .get (path_id ):
994+ call_seq_file = f"{ debug_query_file } .callseq"
995+ try :
996+ with open (call_seq_file , "w" ) as f :
997+ f .write (call_sequence )
998+ if args .verbose >= VERBOSITY_TRACE_COUNTEREXAMPLE :
999+ f .write (f"\n Trace:\n { ctx .traces [path_id ]} " )
1000+ except Exception as e :
1001+ error (f"Could not save call sequence: { e } " )
1002+
9691003
9701004def run_test (ctx : FunctionContext ) -> TestResult :
9711005 args = ctx .args
@@ -1141,26 +1175,26 @@ def run_test(ctx: FunctionContext) -> TestResult:
11411175
11421176 counter = Counter (str (m .result ) for m in ctx .solver_outputs )
11431177 if counter ["sat" ] > 0 :
1144- passfail = red ("[FAIL]" )
1178+ passfail = color_error ("[FAIL]" )
11451179 exitcode = Exitcode .COUNTEREXAMPLE .value
11461180 elif counter ["err" ] > 0 :
1147- passfail = red ("[ERROR]" )
1181+ passfail = color_error ("[ERROR]" )
11481182 exitcode = Exitcode .EXCEPTION .value
11491183 elif counter ["unknown" ] > 0 :
1150- passfail = yellow ("[TIMEOUT]" )
1184+ passfail = color_warn ("[TIMEOUT]" )
11511185 exitcode = Exitcode .TIMEOUT .value
11521186 elif len (stuck ) > 0 :
1153- passfail = red ("[ERROR]" )
1187+ passfail = color_error ("[ERROR]" )
11541188 exitcode = Exitcode .STUCK .value
11551189 elif normal == 0 :
1156- passfail = red ("[ERROR]" )
1190+ passfail = color_error ("[ERROR]" )
11571191 exitcode = Exitcode .REVERT_ALL .value
11581192 warn_code (
11591193 REVERT_ALL ,
11601194 f"{ funsig } : all paths have been reverted; the setup state or inputs may have been too restrictive." ,
11611195 )
11621196 else :
1163- passfail = green ("[PASS]" )
1197+ passfail = color_good ("[PASS]" )
11641198 exitcode = Exitcode .PASS .value
11651199
11661200 timer .stop ()
@@ -1172,6 +1206,12 @@ def run_test(ctx: FunctionContext) -> TestResult:
11721206 f"bounds: [{ ', ' .join ([str (x ) for x in dyn_params ])} ])"
11731207 )
11741208
1209+ if exitcode in (Exitcode .TIMEOUT .value , Exitcode .EXCEPTION .value ):
1210+ # print query directory information
1211+ failure_type = "timeout" if exitcode == Exitcode .TIMEOUT .value else "error"
1212+ query_dir = f"{ dirname (ctx .solving_ctx .dump_dir )} -{ failure_type } "
1213+ print (color_info (f"{ failure_type .capitalize ()} queries saved in: { query_dir } " ))
1214+
11751215 for path_id , _ , err in stuck :
11761216 warn_code (INTERNAL_ERROR , f"Encountered { type (err ).__name__ } : { err } " )
11771217 if args .print_blocked_states :
0 commit comments