2727# But `kani list` runs on this fork, so it can still see it and add it to the total functions under contract.
2828# - See #TODOs for known limitations.
2929
30+ def str_to_bool (string : str ):
31+ match string .strip ().lower ():
32+ case "true" :
33+ return True
34+ case "false" :
35+ return False
36+ case _:
37+ print (f"Unexpected to-be-Boolean string { string } " )
38+ sys .exit (1 )
39+
40+
3041# Process the results from Kani's std-analysis.sh script for each crate.
3142class GenericSTDMetrics ():
3243 def __init__ (self , results_dir , crate ):
@@ -36,8 +47,11 @@ def __init__(self, results_dir, crate):
3647 self .safe_abstractions_count = 0
3748 self .safe_fns_count = 0
3849 self .unsafe_fns = []
50+ self .unsafe_fns_with_loop = []
3951 self .safe_abstractions = []
52+ self .safe_abstractions_with_loop = []
4053 self .safe_fns = []
54+ self .safe_fns_with_loop = []
4155
4256 self .read_std_analysis ()
4357
@@ -55,7 +69,7 @@ def read_overall_counts(self):
5569 # Read {crate}_scan_functions.csv
5670 # and return an array of the unsafe functions and the safe abstractions
5771 def read_scan_functions (self ):
58- expected_header_start = "name;is_unsafe;has_unsafe_ops"
72+ expected_header_start = "name;is_unsafe;has_unsafe_ops;has_unsupported_input;has_loop "
5973 file_path = f"{ self .results_directory } /{ self .crate } _scan_functions.csv"
6074
6175 with open (file_path , 'r' ) as f :
@@ -64,25 +78,31 @@ def read_scan_functions(self):
6478 # The row parsing logic below assumes the column structure in expected_header_start,
6579 # so assert that is how the header begins before continuing
6680 header = next (csv_reader )
67- header_str = ';' .join (header [:3 ])
81+ header_str = ';' .join (header [:5 ])
6882 if not header_str .startswith (expected_header_start ):
6983 print (f"Error: Unexpected CSV header in { file_path } " )
7084 print (f"Expected header to start with: { expected_header_start } " )
7185 print (f"Actual header: { header_str } " )
7286 sys .exit (1 )
7387
7488 for row in csv_reader :
75- if len (row ) >= 3 :
89+ if len (row ) >= 5 :
7690 name , is_unsafe , has_unsafe_ops = row [0 ], row [1 ], row [2 ]
91+ has_unsupported_input , has_loop = row [3 ], row [4 ]
7792 # An unsafe function is a function for which is_unsafe=true
78- if is_unsafe . strip () == "true" :
93+ if str_to_bool ( is_unsafe ) :
7994 self .unsafe_fns .append (name )
95+ if str_to_bool (has_loop ):
96+ self .unsafe_fns_with_loop .append (name )
8097 else :
81- assert is_unsafe .strip () == "false" # sanity check against malformed data
8298 self .safe_fns .append (name )
99+ if str_to_bool (has_loop ):
100+ self .safe_fns_with_loop .append (name )
83101 # A safe abstraction is a safe function with unsafe ops
84- if has_unsafe_ops . strip () == "true" :
102+ if str_to_bool ( has_unsafe_ops ) :
85103 self .safe_abstractions .append (name )
104+ if str_to_bool (has_loop ):
105+ self .safe_abstractions_with_loop .append (name )
86106
87107 def read_std_analysis (self ):
88108 self .read_overall_counts ()
@@ -143,9 +163,30 @@ class KaniSTDMetricsOverTime():
143163 def __init__ (self , metrics_file , crate ):
144164 self .crate = crate
145165 self .dates = []
146- self .unsafe_metrics = ['total_unsafe_fns' , 'unsafe_fns_under_contract' , 'verified_unsafe_fns_under_contract' ]
147- self .safe_abstr_metrics = ['total_safe_abstractions' , 'safe_abstractions_under_contract' , 'verified_safe_abstractions_under_contract' ]
148- self .safe_metrics = ['total_safe_fns' , 'safe_fns_under_contract' , 'verified_safe_fns_under_contract' ]
166+ self .unsafe_metrics = [
167+ 'total_unsafe_fns' ,
168+ 'total_unsafe_fns_with_loop' ,
169+ 'unsafe_fns_under_contract' ,
170+ 'unsafe_fns_with_loop_under_contract' ,
171+ 'verified_unsafe_fns_under_contract' ,
172+ 'verified_unsafe_fns_with_loop_under_contract'
173+ ]
174+ self .safe_abstr_metrics = [
175+ 'total_safe_abstractions' ,
176+ 'total_safe_abstractions_with_loop'
177+ 'safe_abstractions_under_contract' ,
178+ 'safe_abstractions_with_loop_under_contract' ,
179+ 'verified_safe_abstractions_under_contract' ,
180+ 'verified_safe_abstractions_with_loop_under_contract'
181+ ]
182+ self .safe_metrics = [
183+ 'total_safe_fns' ,
184+ 'total_safe_fns_with_loop' ,
185+ 'safe_fns_under_contract' ,
186+ 'safe_fns_with_loop_under_contract' ,
187+ 'verified_safe_fns_under_contract' ,
188+ 'verified_safe_fns_with_loop_under_contract'
189+ ]
149190 # The keys in these dictionaries are unsafe_metrics, safe_abstr_metrics, and safe_metrics, respectively; see update_plot_metrics()
150191 self .unsafe_plot_data = defaultdict (list )
151192 self .safe_abstr_plot_data = defaultdict (list )
@@ -193,35 +234,62 @@ def compute_metrics(self, kani_list_filepath, analysis_results_dir):
193234 print ("Comparing kani-list output to std-analysis.sh output and computing metrics..." )
194235
195236 (unsafe_fns_under_contract , verified_unsafe_fns_under_contract ) = (0 , 0 )
237+ unsafe_fns_with_loop_under_contract = 0
238+ verified_unsafe_fns_with_loop_under_contract = 0
196239 (safe_abstractions_under_contract , verified_safe_abstractions_under_contract ) = (0 , 0 )
240+ safe_abstractions_with_loop_under_contract = 0
241+ verified_safe_abstractions_with_loop_under_contract = 0
197242 (safe_fns_under_contract , verified_safe_fns_under_contract ) = (0 , 0 )
243+ safe_fns_with_loop_under_contract = 0
244+ verified_safe_fns_with_loop_under_contract = 0
198245
199246 for (func_under_contract , has_harnesses ) in kani_data .fns_under_contract :
200247 if func_under_contract in generic_metrics .unsafe_fns :
248+ has_loop = int (func_under_contract in
249+ generic_metrics .unsafe_fns_with_loop )
201250 unsafe_fns_under_contract += 1
251+ unsafe_fns_with_loop_under_contract += has_loop
202252 if has_harnesses :
203253 verified_unsafe_fns_under_contract += 1
254+ verified_unsafe_fns_with_loop_under_contract += has_loop
204255 if func_under_contract in generic_metrics .safe_abstractions :
256+ has_loop = int (func_under_contract in
257+ generic_metrics .safe_abstractions_with_loop )
205258 safe_abstractions_under_contract += 1
259+ safe_abstractions_with_loop_under_contract += has_loop
206260 if has_harnesses :
207261 verified_safe_abstractions_under_contract += 1
262+ verified_safe_abstractions_with_loop_under_contract += has_loop
208263 if func_under_contract in generic_metrics .safe_fns :
264+ has_loop = int (func_under_contract in
265+ generic_metrics .safe_fns_with_loop )
209266 safe_fns_under_contract += 1
267+ safe_fns_with_loop_under_contract += has_loop
210268 if has_harnesses :
211269 verified_safe_fns_under_contract += 1
270+ verified_safe_fns_with_loop_under_contract += has_loop
212271
213272 # Keep the keys here in sync with unsafe_metrics, safe_metrics, and safe_abstr_metrics
214273 data = {
215274 "date" : self .date ,
216275 "total_unsafe_fns" : generic_metrics .unsafe_fns_count ,
276+ "total_unsafe_fns_with_loop" : len (generic_metrics .unsafe_fns_with_loop ),
217277 "total_safe_abstractions" : generic_metrics .safe_abstractions_count ,
278+ "total_safe_abstractions_with_loop" : len (generic_metrics .total_safe_abstractions_with_loop )
218279 "total_safe_fns" : generic_metrics .safe_fns_count ,
280+ "total_safe_fns_with_loop" : len (generic_metrics .total_safe_fns_with_loop )
219281 "unsafe_fns_under_contract" : unsafe_fns_under_contract ,
282+ "unsafe_fns_with_loop_under_contract" : unsafe_fns_with_loop_under_contract ,
220283 "verified_unsafe_fns_under_contract" : verified_unsafe_fns_under_contract ,
284+ "verified_unsafe_fns_with_loop_under_contract" : verified_unsafe_fns_with_loop_under_contract ,
221285 "safe_abstractions_under_contract" : safe_abstractions_under_contract ,
286+ "safe_abstractions_with_loop_under_contract" : safe_abstractions_with_loop_under_contract ,
222287 "verified_safe_abstractions_under_contract" : verified_safe_abstractions_under_contract ,
288+ "verified_safe_abstractions_with_loop_under_contract" : verified_safe_abstractions_with_loop_under_contract ,
223289 "safe_fns_under_contract" : safe_fns_under_contract ,
290+ "safe_fns_with_loop_under_contract" : safe_fns_with_loop_under_contract ,
224291 "verified_safe_fns_under_contract" : verified_safe_fns_under_contract ,
292+ "verified_safe_fns_with_loop_under_contract" : verified_safe_fns_with_loop_under_contract ,
225293 "total_functions_under_contract" : kani_data .total_fns_under_contract ,
226294 }
227295
@@ -241,7 +309,27 @@ def compute_metrics(self, kani_list_filepath, analysis_results_dir):
241309 def plot_single (self , data , title , filename , plot_dir ):
242310 plt .figure (figsize = (14 , 8 ))
243311
244- colors = ['#1f77b4' , '#ff7f0e' , '#2ca02c' , '#d62728' , '#946F7bd' , '#8c564b' , '#e377c2' , '#7f7f7f' , '#bcbd22' , '#17becf' ]
312+ colors = [
313+ '#1f77b4' , #total_unsafe_fns
314+ '#941fb4' , #total_unsafe_fns_with_loop
315+ '#ff7f0e' , #total_safe_abstractions
316+ '#abff0e' , #total_safe_abstractions_with_loop
317+ '#2ca02c' , #total_safe_fns
318+ '#a02c8d' , #total_safe_fns_with_loop
319+ '#d62728' , #unsafe_fns_under_contract
320+ '#27d6aa' , #unsafe_fns_with_loop_under_contract
321+ '#9467bd' , #verified_unsafe_fns_under_contract
322+ '#67acbd' , #verified_unsafe_fns_with_loop_under_contract
323+ '#8c564b' , #safe_abstractions_under_contract
324+ '#8c814b' , #safe_abstractions_with_loop_under_contract
325+ '#e377c2' , #verified_safe_abstractions_under_contract
326+ '#a277e3' , #verified_safe_abstractions_with_loop_under_contract
327+ '#7f7f7f' , #safe_fns_under_contract
328+ '#9e6767' , #safe_fns_with_loop_under_contract
329+ '#bcbd22' , #verified_safe_fns_under_contract
330+ '#49bd22' , #verified_safe_fns_with_loop_under_contract
331+ '#17becf' #total_functions_under_contract
332+ ]
245333
246334 for i , (metric , values ) in enumerate (data .items ()):
247335 color = colors [i % len (colors )]
0 commit comments