Skip to content

Commit 39cfb97

Browse files
committed
add workflow to compute and publish the fuzz coverage
1 parent ae8f926 commit 39cfb97

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

.github/workflows/coverage.yaml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: Fuzz Coverage
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "corpus/**"
9+
10+
schedule:
11+
- cron: "0 3 * * *" # Daily at 03:00 UTC
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: write
16+
pages: write
17+
id-token: write
18+
19+
jobs:
20+
coverage:
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- name: Checkout corpus repo
25+
uses: actions/checkout@v4
26+
27+
- name: Checkout fuzz targets repo
28+
uses: actions/checkout@v4
29+
with:
30+
repository: stratum-mining/stratum
31+
path: stratum
32+
33+
- name: Install Rust
34+
uses: dtolnay/rust-toolchain@nightly
35+
with:
36+
profile: minimal
37+
toolchain: nightly
38+
override: true
39+
components: llvm-tools-preview
40+
41+
- name: Add Rust LLVM tools to PATH
42+
run: |
43+
LLVM_TOOLS=$(rustc +nightly --print sysroot)/lib/rustlib/$(rustc --print host-tuple)/bin
44+
echo "$LLVM_TOOLS" >> $GITHUB_PATH
45+
ls -al "$LLVM_TOOLS"
46+
47+
- name: Install cargo-fuzz
48+
run: cargo install cargo-fuzz
49+
50+
- name: Copy corpus into fuzz targets repo
51+
run: |
52+
mkdir -p stratum/fuzz/corpus
53+
mv corpus/* stratum/fuzz/corpus
54+
55+
- name: Run fuzz coverage
56+
run: |
57+
chmod +x ./scripts/fuzz_coverage.sh
58+
cp ./scripts/fuzz_coverage.sh stratum/fuzz_coverage.sh
59+
cd stratum/
60+
ls fuzz/
61+
./fuzz_coverage.sh
62+
63+
- name: Move coverage results into this repo
64+
run: |
65+
rm -rf coverage
66+
mkdir -p coverage
67+
cp -r stratum/coverage_html coverage/
68+
69+
- name: Upload pages artifact
70+
uses: actions/upload-pages-artifact@v3
71+
with:
72+
path: coverage/coverage_html
73+
74+
deploy:
75+
needs: coverage
76+
runs-on: ubuntu-latest
77+
environment:
78+
name: github-pages
79+
url: ${{ steps.deploy.outputs.page_url }}
80+
81+
steps:
82+
- name: Deploy to GitHub Pages
83+
id: deploy
84+
uses: actions/deploy-pages@v4
85+

scripts/fuzz_coverage.sh

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
############################################
5+
# Tools must be available directly in PATH:
6+
# llvm-profdata
7+
# llvm-cov
8+
############################################
9+
10+
PROJECT_ROOT="$(pwd)"
11+
HOST_TARGET=$(rustc +nightly --print host-tuple)
12+
13+
echo "[+] Project root: $PROJECT_ROOT"
14+
echo "[+] Host target: $HOST_TARGET"
15+
16+
############################################
17+
# 1. Run coverage for all fuzz targets
18+
############################################
19+
20+
echo "[+] Running fuzz coverage"
21+
for target in $(cargo +nightly fuzz list); do
22+
echo "$target"
23+
cargo +nightly fuzz coverage "$target"
24+
done
25+
26+
############################################
27+
# 2. Merge all .profdata
28+
############################################
29+
30+
echo "[+] Merging coverage profiles"
31+
llvm-profdata merge -sparse \
32+
fuzz/coverage/*/coverage.profdata \
33+
-o fuzz/coverage/merged.profdata
34+
35+
############################################
36+
# 3. Collect fuzz target coverage binaries
37+
############################################
38+
39+
echo "[+] Collecting fuzz binaries"
40+
41+
OBJECTS=""
42+
FIRST_TARGET=""
43+
44+
for target in $(cargo fuzz list); do
45+
BIN="target/$HOST_TARGET/coverage/$HOST_TARGET/release/$target"
46+
47+
if [ ! -f "$BIN" ]; then
48+
echo "ERROR: expected binary not found: $BIN"
49+
exit 1
50+
fi
51+
52+
if [ -z "$FIRST_TARGET" ]; then
53+
FIRST_TARGET="$BIN"
54+
else
55+
OBJECTS="$OBJECTS -object $BIN"
56+
fi
57+
done
58+
59+
############################################
60+
# 4. Filtering rules:
61+
# - ignore stdlib (/rustc/…)
62+
# - ignore crates.io (~/.cargo/registry/…)
63+
# - ignore everything outside the project dir
64+
############################################
65+
66+
IGNORE_FLAGS=(
67+
"-ignore-filename-regex=/.*/rustc/.*"
68+
"-ignore-filename-regex=/.cargo/registry/.*"
69+
"-ignore-filename-regex=^/(?!${PROJECT_ROOT}).*$"
70+
)
71+
72+
############################################
73+
# 5. Generate text summary
74+
############################################
75+
76+
echo "[+] Generating text summary"
77+
llvm-cov report \
78+
"${IGNORE_FLAGS[@]}" \
79+
-instr-profile=fuzz/coverage/merged.profdata \
80+
$FIRST_TARGET $OBJECTS \
81+
> coverage_summary.txt
82+
83+
############################################
84+
# 6. Generate HTML report
85+
############################################
86+
87+
echo "[+] Generating HTML report"
88+
llvm-cov show \
89+
"${IGNORE_FLAGS[@]}" \
90+
-instr-profile=fuzz/coverage/merged.profdata \
91+
$FIRST_TARGET $OBJECTS \
92+
-format=html \
93+
-output-dir=coverage_html
94+
95+
echo "[✓] Done!"
96+
echo "HTML → coverage_html/index.html"
97+
echo "TXT → coverage_summary.txt"
98+

scripts/readme.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Fuzz Coverage Script
2+
3+
This repository includes a helper script, `fuzz_coverage.sh`, that lets you generate local coverage reports for our fuzz targets.
4+
5+
The script is meant to be copied into the **root of the [stratum](https://github.com/stratum-mining/stratum) repository**.
6+
7+
It also expects that the **corpus directory** of this repository to be present in `fuzz/corpus` on the [stratum](https://github.com/stratum-mining/stratum) repository.
8+
9+
---
10+
11+
## Requirements
12+
13+
Before running the script, the following must be available directly in your `$PATH`:
14+
15+
* `llvm-profdata`
16+
* `llvm-cov`
17+
* A nightly Rust toolchain
18+
* `cargo-fuzz`
19+
20+
For details on installing LLVM coverage tools, see:
21+
[https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools](https://doc.rust-lang.org/rustc/instrument-coverage.html#installing-llvm-coverage-tools)
22+
23+
---
24+
25+
## Expected Project Layout
26+
27+
After copying `fuzz_coverage.sh` into the root of [stratum](https://github.com/stratum-mining/stratum) repository, the structure should look like:
28+
29+
```
30+
stratum/
31+
├── coverage_html (generated by the fuzz_coverage.sh after a successfull run)
32+
├── fuzz
33+
│ ├── artifacts
34+
│ ├── corpus (the one copied from this repo)
35+
│ ├── coverage (generated by cargo-fuzz)
36+
│ ├── fuzz_targets
37+
└── fuzz_coverage.sh (copied from this repo to the root of the stratum repo)
38+
```
39+
40+
---
41+
42+
## Usage
43+
44+
Run the script from the project root:
45+
46+
```bash
47+
./fuzz_coverage.sh
48+
```
49+
50+
The script does the following:
51+
52+
1. Runs `cargo +nightly fuzz coverage` for every fuzz target.
53+
2. Merges all generated `.profdata` files.
54+
3. Collects the built coverage binaries.
55+
5. Generates:
56+
* `coverage_summary.txt` (text report)
57+
* `coverage_html/` (HTML report)
58+
59+
---
60+
61+
## Output
62+
63+
After running:
64+
65+
* Open the HTML report:
66+
67+
```
68+
coverage_html/index.html
69+
```
70+
71+
* View the text summary:
72+
73+
```
74+
coverage_summary.txt
75+
```

0 commit comments

Comments
 (0)