Skip to content

Commit df2e643

Browse files
committed
Plot safe functions (which includes safe abstractions)
1 parent f5a4899 commit df2e643

File tree

2 files changed

+32
-11
lines changed

2 files changed

+32
-11
lines changed

scripts/kani-std-analysis/kani_std_analysis.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ def __init__(self):
2626
self.results_directory = "/tmp/std_lib_analysis/results"
2727
self.unsafe_fns_count = 0
2828
self.safe_abstractions_count = 0
29+
self.safe_fns_count = 0
2930
self.unsafe_fns = []
3031
self.safe_abstractions = []
32+
self.safe_fns = []
3133

3234
self.read_std_analysis()
3335

@@ -40,6 +42,7 @@ def read_overall_counts(self):
4042
counts = {row[0]: int(row[1]) for row in csv_reader if len(row) >= 2}
4143
self.unsafe_fns_count = counts.get('unsafe_fns', 0)
4244
self.safe_abstractions_count = counts.get('safe_abstractions', 0)
45+
self.safe_fns_count = counts.get('safe_fns', 0)
4346

4447
# Read {crate}_scan_functions.csv
4548
# and return an array of the unsafe functions and the safe abstractions
@@ -66,15 +69,18 @@ def read_scan_functions(self):
6669
# An unsafe function is a function for which is_unsafe=true
6770
if is_unsafe.strip() == "true":
6871
self.unsafe_fns.append(name)
69-
# A safe abstraction is a function that is not unsafe (is_unsafe=false) but has unsafe ops
70-
elif is_unsafe.strip() == "false" and has_unsafe_ops.strip() == "true":
71-
self.safe_abstractions.append(name)
72+
else:
73+
assert is_unsafe.strip() == "false" # sanity check against malformed data
74+
self.safe_fns.append(name)
75+
# A safe abstraction is a safe function with unsafe ops
76+
if has_unsafe_ops.strip() == "true":
77+
self.safe_abstractions.append(name)
7278

7379
def read_std_analysis(self):
7480
self.read_overall_counts()
7581
self.read_scan_functions()
7682

77-
# Sanity checks for the CSV parsing
83+
# Sanity checks
7884
if len(self.unsafe_fns) != self.unsafe_fns_count:
7985
print(f"Number of unsafe functions does not match core_scan_functions.csv")
8086
print(f"UNSAFE_FNS_COUNT: {self.unsafe_fns_count}")
@@ -86,6 +92,12 @@ def read_std_analysis(self):
8692
print(f"SAFE_ABSTRACTIONS_COUNT: {self.safe_abstractions_count}")
8793
print(f"SAFE_ABSTRACTIONS length: {len(self.safe_abstractions)}")
8894
sys.exit(1)
95+
96+
if len(self.safe_fns) != self.safe_fns_count:
97+
print(f"Number of safe functions does not match core_scan_functions.csv")
98+
print(f"SAFE_FNS_COUNT: {self.safe_fns_count}")
99+
print(f"SAFE_FNS length: {len(self.safe_fns)}")
100+
sys.exit(1)
89101

90102
# Process the results of running `kani list` against the standard library,
91103
# i.e., the Kani STD metrics for a single date (whichever day this script is running).
@@ -124,10 +136,11 @@ def __init__(self, metrics_file):
124136
self.dates = []
125137
self.unsafe_metrics = ['total_unsafe_fns', 'unsafe_fns_under_contract', 'verified_unsafe_fns_under_contract']
126138
self.safe_abstr_metrics = ['total_safe_abstractions', 'safe_abstractions_under_contract', 'verified_safe_abstractions_under_contract']
127-
# Generate two plots per crate: one with metrics about unsafe functions, and one with metrics about safe abstractions.
139+
self.safe_metrics = ['total_safe_fns', 'safe_fns_under_contract', 'verified_safe_fns_under_contract']
128140
# The keys in these dictionaries are unsafe_metrics and safe_abstr_metrics, respectively; see update_plot_metrics()
129141
self.unsafe_plot_data = defaultdict(list)
130142
self.safe_abstr_plot_data = defaultdict(list)
143+
self.safe_plot_data = defaultdict(list)
131144

132145
self.date = datetime.today().date()
133146
self.metrics_file = metrics_file
@@ -160,6 +173,10 @@ def update_plot_metrics(self, all_data):
160173
for metric in self.safe_abstr_metrics:
161174
if not metric.startswith("verified"):
162175
self.safe_abstr_plot_data[metric].append(data[metric])
176+
177+
for metric in self.safe_metrics:
178+
if not metric.startswith("verified"):
179+
self.safe_plot_data[metric].append(data[metric])
163180

164181
# Read output from kani list and std-analysis.sh, then compare their outputs to compute Kani-specific metrics
165182
# and write the results to {self.metrics_file}
@@ -179,19 +196,21 @@ def compute_metrics(self, kani_list_filepath):
179196
unsafe_fns_under_contract += 1
180197
if has_harnesses:
181198
verified_unsafe_fns_under_contract += 1
182-
elif func_under_contract in generic_metrics.safe_abstractions:
199+
if func_under_contract in generic_metrics.safe_abstractions:
183200
safe_abstractions_under_contract += 1
184201
if has_harnesses:
185202
verified_safe_abstractions_under_contract += 1
186-
else:
203+
if func_under_contract in generic_metrics.safe_fns:
187204
safe_fns_under_contract += 1
188205
if has_harnesses:
189206
verified_safe_fns_under_contract += 1
190207

208+
# Keep the keys here in sync with unsafe_metrics, safe_metrics, and safe_abstr_metrics
191209
data = {
192210
"date": self.date,
193211
"total_unsafe_fns": generic_metrics.unsafe_fns_count,
194212
"total_safe_abstractions": generic_metrics.safe_abstractions_count,
213+
"total_safe_fns": generic_metrics.safe_fns_count,
195214
"unsafe_fns_under_contract": unsafe_fns_under_contract,
196215
"verified_unsafe_fns_under_contract": verified_unsafe_fns_under_contract,
197216
"safe_abstractions_under_contract": safe_abstractions_under_contract,
@@ -252,8 +271,9 @@ def plot_single(self, data, title, outfile):
252271
print(f"PNG graph generated: {outfile}")
253272

254273
def plot(self):
255-
self.plot_single(self.unsafe_plot_data, title="Contracts on Unsafe Functions in core", outfile="core_unsafe_std_lib_metrics.png")
256-
self.plot_single(self.safe_abstr_plot_data, title="Contracts on Safe Abstractions in core", outfile="core_safe_abstractions_std_lib_metrics.png")
274+
self.plot_single(self.unsafe_plot_data, title="Contracts on Unsafe Functions in core", outfile="core_unsafe_metrics.png")
275+
self.plot_single(self.safe_abstr_plot_data, title="Contracts on Safe Abstractions in core", outfile="core_safe_abstractions_metrics.png")
276+
self.plot_single(self.safe_plot_data, title="Contracts on Safe Functions in core", outfile="core_safe_metrics.png")
257277

258278
def main():
259279
parser = argparse.ArgumentParser(description="Generate metrics about Kani's application to the standard library.")

scripts/kani-std-analysis/metrics-data.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
"date": "2025-01-01",
55
"total_unsafe_fns": 6987,
66
"total_safe_abstractions": 1704,
7+
"total_safe_fns": 14666,
78
"unsafe_fns_under_contract": 144,
89
"verified_unsafe_fns_under_contract": 132,
910
"safe_abstractions_under_contract": 41,
1011
"verified_safe_abstractions_under_contract": 41,
11-
"safe_fns_under_contract": 39,
12-
"verified_safe_fns_under_contract": 38,
12+
"safe_fns_under_contract": 80,
13+
"verified_safe_fns_under_contract": 79,
1314
"total_functions_under_contract": 224
1415
}
1516
]

0 commit comments

Comments
 (0)