-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
165 lines (135 loc) · 6.13 KB
/
Copy pathutils.py
File metadata and controls
165 lines (135 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env python3
"""
Provide some convenience methods for statistical evaluation of fuzzing runs
"""
import itertools
import sys
from statistics import mean, median
from scipy.stats import mannwhitneyu # type: ignore
from typing import List, Dict, Tuple
from a12 import a12, effect_size_latex_table_field
def eprint(msg: str) -> None:
print(msg, file=sys.stderr)
def find_best_competitor(tweak: str, fuzzer_to_bbs: Dict[str, List[int]], method: str = 'median') -> List[str]:
"""
Find best competitor of tweak using a specific method (by default the median)
Parameters
----------
tweak : str
name of your fuzzer (the "tweak")
fuzzer_to_bbs: Dict[str, List[int]]
dict that maps fuzzer names to a list, where each element represents the number of total basic blocks
found by one run (i.e., len(list) == num_runs)
methods: str = 'median'
Methods to use to identify the best competitor (supports 'mean' and 'median', the latter being the default)
Returns
-------
best_competitor : List[str]
name of the best competitor(s) -- multiple may exist if they are equal under 'method'
"""
fn = median
if method == "mean":
fn = mean
ratings: Dict[str, float] = {}
for fuzzer, bbs in fuzzer_to_bbs.items():
if fuzzer == tweak:
continue
ratings[fuzzer] = fn(bbs)
max_val = max([v for v in ratings.values()])
best = [f for f, v in ratings.items() if v == max_val]
return best
def mann_whitney_u(baseline: List[int], tweak: List[int], unsafe: bool = False, quiet: bool = False) -> Tuple[float, float]:
if not unsafe:
assert not contains_ties(baseline+tweak), "'exact' Mann-Whitney-U test may not contain ties in data"
elif not quiet:
validate_datasets(baseline, tweak, "baseline", "tweak")
mwu_result = mannwhitneyu(
x=baseline,
y=tweak,
alternative="two-sided",
method="exact"
)
stat: float = mwu_result[0] # type: ignore
p_value: float = mwu_result[1] # type: ignore
return stat, p_value # type: ignore
def compare_against_baseline(fuzzer_data: Dict[str, List[int]], baseline: str) -> None:
assert baseline in fuzzer_data, f"Baseline {baseline} not in data: {fuzzer_data}"
for fuzzer, values in fuzzer_data.items():
if fuzzer == baseline:
continue
effect_size = a12(baseline=fuzzer_data[baseline], tweak=values)
effect_size_field = effect_size_latex_table_field(effect_size)
_, p_val = mann_whitney_u(baseline=fuzzer_data[baseline], tweak=values, unsafe=True)
print(f"{fuzzer:32} (tweak) vs {baseline:16} (baseline): p_val={p_val:.4f} (< 0.05? {str(p_val < 0.05):5}), {effect_size_field}")
def compare_pairwise(fuzzer_data: Dict[str, List[int]]) -> None:
for tw_tup, bl_tup in itertools.permutations(fuzzer_data.items(), 2):
effect_size = a12(baseline=bl_tup[1], tweak=tw_tup[1])
effect_size_field = effect_size_latex_table_field(effect_size)
_, p_val = mann_whitney_u(baseline=bl_tup[1], tweak=tw_tup[1], unsafe=True)
print(f"{tw_tup[0]:10} (tweak) vs {bl_tup[0]:10} (baseline): p_val={p_val:.4f} (< 0.05? {str(p_val < 0.05):5}), {effect_size_field}")
# best_competitor_data.append((bl_tup, p_val, effect_size))
#return best_competitor_data
def compare_against_best_competitor(tweak: str, fuzzer_data: Dict[str, List[int]]) -> List[Tuple[str, float, float]]:
best_competitor = find_best_competitor(tweak, fuzzer_data)
if len(best_competitor) != 1:
print(f"[i] Found {len(best_competitor)} best competitors: {best_competitor}")
best_competitor_data: List[Tuple[str, float, float]] = []
for comp in best_competitor:
effect_size = a12(baseline=fuzzer_data[comp], tweak=fuzzer_data[tweak])
effect_size_field = effect_size_latex_table_field(effect_size)
_, p_val = mann_whitney_u(baseline=fuzzer_data[comp], tweak=fuzzer_data[tweak], unsafe=True)
print(f"{comp}: p_val={p_val:.4f} (< 0.05? {p_val < 0.05}), {effect_size_field}")
best_competitor_data.append((comp, p_val, effect_size))
return best_competitor_data
def contains_zero(data: List[int]) -> bool:
"""
Check whether the data contains zero as value
"""
if 0 in data:
return True
return False
def contains_ties(data: List[int]) -> bool:
"""
Check whether data contains ties, i.e., one (or more) elements are contained more than once
"""
if len(set(data)) != len(data):
return True
return False
def num_ties(data: List[int]) -> int:
return len(data) - len(set(data))
def contains_overlaps(data_a: List[int], data_b: List[int]) -> bool:
return len(set(data_a).intersection(set(data_b))) > 0
def validate_data(data: List[int], label: str) -> bool:
"""
Check and warn if data contains zeroes or ties
"""
is_valid = True
if contains_zero(data):
print(f"[!] Data of {label} contains 0 ({data.count(0)} times)")
is_valid = False
if contains_ties(data):
for e in set(data):
if data.count(e) > 1:
print(f"[!] Data of {label} contains {e} {data.count(e)} times")
is_valid = False
return is_valid
def validate_datasets(data_a: List[int], data_b: List[int], label_a: str, label_b: str) -> bool:
is_valid = True
if contains_ties(data_a):
eprint(f"[!] {label_a} contains {num_ties(data_a)} ties: {sorted(data_a)}")
is_valid = False
if contains_ties(data_b):
eprint(f"[!] {label_b} contains {num_ties(data_b)} ties: {sorted(data_b)}")
is_valid = False
if contains_ties(data_a + data_b):
eprint(f"[!] {label_a}+{label_b} contains {num_ties(data_a + data_b)} ties: {sorted(data_a + data_b)}")
is_valid = False
return is_valid
if __name__ == "__main__":
fuzzer_to_bbs = {
"tweak" : [12001, 10345, 11453, 11990, 12040],
"fuzzer-1" : [11377, 11707, 11731, 11899, 12178],
"fuzzer-2" : [11703, 11791, 12030, 12039, 12135],
"fuzzer-3" : [12340, 10000, 15000, 9000, 11132]
}
print(find_best_competitor("tweak", fuzzer_to_bbs))