@@ -81,13 +81,201 @@ jobs:
81
81
path : |
82
82
fuzz/corpus/${{ matrix.test-target.name }}
83
83
- name : Run ${{ matrix.test-target.name }} for XX seconds
84
+ id : run_fuzzer
84
85
shell : bash
85
86
continue-on-error : ${{ !matrix.test-target.name.should_pass }}
86
87
run : |
87
- cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -timeout=${{ env.RUN_FOR }} -detect_leaks=0
88
+ mkdir -p fuzz/stats
89
+ STATS_FILE="fuzz/stats/${{ matrix.test-target.name }}.txt"
90
+ cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -timeout=${{ env.RUN_FOR }} -detect_leaks=0 -print_final_stats=1 2>&1 | tee "$STATS_FILE"
91
+
92
+ # Extract key stats from the output
93
+ if grep -q "stat::number_of_executed_units" "$STATS_FILE"; then
94
+ RUNS=$(grep "stat::number_of_executed_units" "$STATS_FILE" | awk '{print $2}')
95
+ echo "runs=$RUNS" >> "$GITHUB_OUTPUT"
96
+ else
97
+ echo "runs=unknown" >> "$GITHUB_OUTPUT"
98
+ fi
99
+
100
+ if grep -q "stat::average_exec_per_sec" "$STATS_FILE"; then
101
+ EXEC_RATE=$(grep "stat::average_exec_per_sec" "$STATS_FILE" | awk '{print $2}')
102
+ echo "exec_rate=$EXEC_RATE" >> "$GITHUB_OUTPUT"
103
+ else
104
+ echo "exec_rate=unknown" >> "$GITHUB_OUTPUT"
105
+ fi
106
+
107
+ if grep -q "stat::new_units_added" "$STATS_FILE"; then
108
+ NEW_UNITS=$(grep "stat::new_units_added" "$STATS_FILE" | awk '{print $2}')
109
+ echo "new_units=$NEW_UNITS" >> "$GITHUB_OUTPUT"
110
+ else
111
+ echo "new_units=unknown" >> "$GITHUB_OUTPUT"
112
+ fi
113
+
114
+ # Save should_pass value to file for summary job to use
115
+ echo "${{ matrix.test-target.name.should_pass }}" > "fuzz/stats/${{ matrix.test-target.name }}.should_pass"
116
+
117
+ # Print stats to job output for immediate visibility
118
+ echo "----------------------------------------"
119
+ echo "FUZZING STATISTICS FOR ${{ matrix.test-target.name }}"
120
+ echo "----------------------------------------"
121
+ echo "Runs: $(grep -q "stat::number_of_executed_units" "$STATS_FILE" && grep "stat::number_of_executed_units" "$STATS_FILE" | awk '{print $2}' || echo "unknown")"
122
+ echo "Execution Rate: $(grep -q "stat::average_exec_per_sec" "$STATS_FILE" && grep "stat::average_exec_per_sec" "$STATS_FILE" | awk '{print $2}' || echo "unknown") execs/sec"
123
+ echo "New Units: $(grep -q "stat::new_units_added" "$STATS_FILE" && grep "stat::new_units_added" "$STATS_FILE" | awk '{print $2}' || echo "unknown")"
124
+ echo "Expected: ${{ matrix.test-target.name.should_pass }}"
125
+ if grep -q "SUMMARY: " "$STATS_FILE"; then
126
+ echo "Status: $(grep "SUMMARY: " "$STATS_FILE" | head -1)"
127
+ else
128
+ echo "Status: Completed"
129
+ fi
130
+ echo "----------------------------------------"
131
+
132
+ # Add summary to GitHub step summary
133
+ echo "### Fuzzing Results for ${{ matrix.test-target.name }}" >> $GITHUB_STEP_SUMMARY
134
+ echo "" >> $GITHUB_STEP_SUMMARY
135
+ echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY
136
+ echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
137
+
138
+ if grep -q "stat::number_of_executed_units" "$STATS_FILE"; then
139
+ echo "| Runs | $(grep "stat::number_of_executed_units" "$STATS_FILE" | awk '{print $2}') |" >> $GITHUB_STEP_SUMMARY
140
+ fi
141
+
142
+ if grep -q "stat::average_exec_per_sec" "$STATS_FILE"; then
143
+ echo "| Execution Rate | $(grep "stat::average_exec_per_sec" "$STATS_FILE" | awk '{print $2}') execs/sec |" >> $GITHUB_STEP_SUMMARY
144
+ fi
145
+
146
+ if grep -q "stat::new_units_added" "$STATS_FILE"; then
147
+ echo "| New Units | $(grep "stat::new_units_added" "$STATS_FILE" | awk '{print $2}') |" >> $GITHUB_STEP_SUMMARY
148
+ fi
149
+
150
+ echo "| Expected | ${{ matrix.test-target.name.should_pass }} |" >> $GITHUB_STEP_SUMMARY
151
+
152
+ if grep -q "SUMMARY: " "$STATS_FILE"; then
153
+ echo "| Status | $(grep "SUMMARY: " "$STATS_FILE" | head -1) |" >> $GITHUB_STEP_SUMMARY
154
+ else
155
+ echo "| Status | Completed |" >> $GITHUB_STEP_SUMMARY
156
+ fi
157
+
158
+ echo "" >> $GITHUB_STEP_SUMMARY
88
159
- name : Save Corpus Cache
89
160
uses : actions/cache/save@v4
90
161
with :
91
162
key : corpus-cache-${{ matrix.test-target.name }}
92
163
path : |
93
164
fuzz/corpus/${{ matrix.test-target.name }}
165
+ - name : Upload Stats
166
+ uses : actions/upload-artifact@v4
167
+ with :
168
+ name : fuzz-stats-${{ matrix.test-target.name }}
169
+ path : |
170
+ fuzz/stats/${{ matrix.test-target.name }}.txt
171
+ fuzz/stats/${{ matrix.test-target.name }}.should_pass
172
+ retention-days : 5
173
+ fuzz-summary :
174
+ needs : fuzz-run
175
+ name : Fuzzing Summary
176
+ runs-on : ubuntu-latest
177
+ if : always()
178
+ steps :
179
+ - uses : actions/checkout@v4
180
+ with :
181
+ persist-credentials : false
182
+ - name : Download all stats
183
+ uses : actions/download-artifact@v4
184
+ with :
185
+ path : fuzz/stats-artifacts
186
+ pattern : fuzz-stats-*
187
+ merge-multiple : true
188
+ - name : Prepare stats directory
189
+ run : |
190
+ mkdir -p fuzz/stats
191
+ # Debug: List content of stats-artifacts directory
192
+ echo "Contents of stats-artifacts directory:"
193
+ find fuzz/stats-artifacts -type f | sort
194
+
195
+ # Extract files from the artifact directories - handle nested directories
196
+ find fuzz/stats-artifacts -type f -name "*.txt" -exec cp {} fuzz/stats/ \;
197
+ find fuzz/stats-artifacts -type f -name "*.should_pass" -exec cp {} fuzz/stats/ \;
198
+
199
+ # Debug information
200
+ echo "Contents of stats directory after extraction:"
201
+ ls -la fuzz/stats/
202
+ echo "Contents of should_pass files (if any):"
203
+ cat fuzz/stats/*.should_pass 2>/dev/null || echo "No should_pass files found"
204
+ - name : Generate Summary
205
+ run : |
206
+ echo "# Fuzzing Summary" > fuzzing_summary.md
207
+ echo "" >> fuzzing_summary.md
208
+ echo "| Target | Runs | Exec/sec | New Units | Expected | Status |" >> fuzzing_summary.md
209
+ echo "|--------|------|----------|-----------|----------|--------|" >> fuzzing_summary.md
210
+
211
+ TOTAL_RUNS=0
212
+ TOTAL_NEW_UNITS=0
213
+
214
+ for stat_file in fuzz/stats/*.txt; do
215
+ TARGET=$(basename "$stat_file" .txt)
216
+ SHOULD_PASS_FILE="${stat_file%.*}.should_pass"
217
+
218
+ # Get expected status
219
+ if [ -f "$SHOULD_PASS_FILE" ]; then
220
+ EXPECTED=$(cat "$SHOULD_PASS_FILE")
221
+ else
222
+ EXPECTED="unknown"
223
+ fi
224
+
225
+ # Extract runs
226
+ if grep -q "stat::number_of_executed_units" "$stat_file"; then
227
+ RUNS=$(grep "stat::number_of_executed_units" "$stat_file" | awk '{print $2}')
228
+ TOTAL_RUNS=$((TOTAL_RUNS + RUNS))
229
+ else
230
+ RUNS="unknown"
231
+ fi
232
+
233
+ # Extract execution rate
234
+ if grep -q "stat::average_exec_per_sec" "$stat_file"; then
235
+ EXEC_RATE=$(grep "stat::average_exec_per_sec" "$stat_file" | awk '{print $2}')
236
+ else
237
+ EXEC_RATE="unknown"
238
+ fi
239
+
240
+ # Extract new units added
241
+ if grep -q "stat::new_units_added" "$stat_file"; then
242
+ NEW_UNITS=$(grep "stat::new_units_added" "$stat_file" | awk '{print $2}')
243
+ if [[ "$NEW_UNITS" =~ ^[0-9]+$ ]]; then
244
+ TOTAL_NEW_UNITS=$((TOTAL_NEW_UNITS + NEW_UNITS))
245
+ fi
246
+ else
247
+ NEW_UNITS="unknown"
248
+ fi
249
+
250
+ # Extract status
251
+ if grep -q "SUMMARY: " "$stat_file"; then
252
+ STATUS=$(grep "SUMMARY: " "$stat_file" | head -1)
253
+ else
254
+ STATUS="Completed"
255
+ fi
256
+
257
+ echo "| $TARGET | $RUNS | $EXEC_RATE | $NEW_UNITS | $EXPECTED | $STATUS |" >> fuzzing_summary.md
258
+ done
259
+
260
+ echo "" >> fuzzing_summary.md
261
+ echo "## Overall Statistics" >> fuzzing_summary.md
262
+ echo "" >> fuzzing_summary.md
263
+ echo "- **Total runs:** $TOTAL_RUNS" >> fuzzing_summary.md
264
+ echo "- **Total new units discovered:** $TOTAL_NEW_UNITS" >> fuzzing_summary.md
265
+ echo "- **Average execution rate:** $(grep -h "stat::average_exec_per_sec" fuzz/stats/*.txt | awk '{sum += $2; count++} END {if (count > 0) print sum/count " execs/sec"; else print "unknown"}')" >> fuzzing_summary.md
266
+
267
+ # Add count by expected status
268
+ echo "- **Tests expected to pass:** $(find fuzz/stats -name "*.should_pass" -exec cat {} \; | grep -c "true")" >> fuzzing_summary.md
269
+ echo "- **Tests expected to fail:** $(find fuzz/stats -name "*.should_pass" -exec cat {} \; | grep -c "false")" >> fuzzing_summary.md
270
+
271
+ # Write to GitHub step summary
272
+ cat fuzzing_summary.md >> $GITHUB_STEP_SUMMARY
273
+ - name : Show Summary
274
+ run : |
275
+ cat fuzzing_summary.md
276
+ - name : Upload Summary
277
+ uses : actions/upload-artifact@v4
278
+ with :
279
+ name : fuzzing-summary
280
+ path : fuzzing_summary.md
281
+ retention-days : 5
0 commit comments