-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add nightlyfuzz CI #11934
Add nightlyfuzz CI #11934
Changes from 9 commits
b3b509c
47e6a66
c6a8a64
1889db5
cc624d3
b44f197
15f3d88
231f921
a2f34c8
1a53de8
34c2cb2
9bdf380
8fb7094
f666158
1273be6
715f9cb
774a80c
11042a6
f97ad72
5d11ad6
2872e94
81555cc
471b058
97a647d
436cd4a
2613326
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
name: 'nightly/tag fuzz' | ||
on: | ||
schedule: | ||
# Note: The schedule event can be delayed during periods of high | ||
# loads of GitHub Actions workflow runs. High load times include | ||
# the start of every hour. To decrease the chance of delay, | ||
# schedule your workflow to run at a different time of the hour. | ||
- cron: "25 0 * * *" # at 25 past midnight every day | ||
push: | ||
tags: | ||
- '*' | ||
workflow_dispatch: null | ||
jobs: | ||
fuzzrun: | ||
name: "run native fuzzers" | ||
runs-on: "ubuntu20.04-4cores-16GB" | ||
steps: | ||
- name: "Checkout" | ||
uses: "actions/checkout@v3" | ||
- name: "Setup go" | ||
uses: "actions/setup-go@v3" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here: `actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 |
||
with: | ||
go-version-file: 'go.mod' | ||
cache: true | ||
cache-dependency-path: 'go.sum' | ||
- name: "Get corpus directory" | ||
id: "get-corpus-dir" | ||
run: echo "corpus_dir=$(go env GOCACHE)/fuzz" >> $GITHUB_OUTPUT | ||
shell: "bash" | ||
- name: "Restore corpus" | ||
uses: "actions/cache/restore@v3" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here:
|
||
id: "restore-corpus" | ||
with: | ||
path: "${{ steps.get-corpus-dir.outputs.corpus_dir }}" | ||
# We need to ensure uniqueness of the key, as saving to a key more than once will fail (see Save corpus step). | ||
# We never expect a cache hit with the key but we do expect a hit with the restore-keys prefix that is going | ||
# to match the latest cache that has that prefix. | ||
key: "nightlyfuzz-corpus-${{ github.run_id }}-${{ github.run_attempt }}" | ||
restore-keys: "nightlyfuzz-corpus-" | ||
- name: "Run native fuzzers" | ||
# Fuzz for 1 hour | ||
run: "cd fuzz && ./fuzz_all_native.py --seconds 3600" | ||
- name: "Print failing testcases" | ||
if: failure() | ||
run: find . -type f|fgrep '/testdata/fuzz/'|while read f; do echo $f; cat $f; done | ||
- name: "Save corpus" | ||
uses: "actions/cache/save@v3" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here:
|
||
# We save also on failure, so that we can keep the valuable corpus generated that led to finding a crash. | ||
# If the corpus gets clobbered for any reason, we can remove the offending cache from the Github UI. | ||
if: always() | ||
with: | ||
path: "${{ steps.get-corpus-dir.outputs.corpus_dir }}" | ||
key: "${{ steps.restore-corpus.outputs.cache-primary-key }}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*/fuzzer |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import argparse | ||
import itertools | ||
import os | ||
import re | ||
import subprocess | ||
import sys | ||
|
||
LIBROOT = "../" | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
formatter_class=argparse.RawDescriptionHelpFormatter, | ||
description="\n".join([ | ||
"Fuzz helper to run all native go fuzzers in chainlink", | ||
"", | ||
]), | ||
) | ||
parser.add_argument("--ci", required=False, help="In CI mode we run each parser only briefly once", action="store_true") | ||
parser.add_argument("--seconds", required=False, help="Run for this many seconds of total fuzz time before exiting") | ||
args = parser.parse_args() | ||
|
||
# use float for remaining_seconds so we can represent infinity | ||
if args.seconds: | ||
remaining_seconds = float(args.seconds) | ||
else: | ||
remaining_seconds = float("inf") | ||
|
||
fuzzers = discover_fuzzers() | ||
print(f"🐝 Discovered fuzzers:", file=sys.stderr) | ||
for fuzzfn, path in fuzzers.items(): | ||
print(f"{fuzzfn} in {path}", file=sys.stderr) | ||
|
||
if args.ci: | ||
# only run each fuzzer once for 60 seconds in CI | ||
durations_seconds = [60] | ||
else: | ||
# run forever or until --seconds, with increasingly longer durations per fuzz run | ||
durations_seconds = itertools.chain([5, 10, 30, 90, 270], itertools.repeat(600)) | ||
|
||
for duration_seconds in durations_seconds: | ||
print(f"🐝 Running each fuzzer for {duration_seconds}s before switching to next fuzzer", file=sys.stderr) | ||
for fuzzfn, path in fuzzers.items(): | ||
if remaining_seconds <= 0: | ||
print(f"🐝 Time budget of {args.seconds}s is exhausted. Exiting.", file=sys.stderr) | ||
return | ||
|
||
next_duration_seconds = min(remaining_seconds, duration_seconds) | ||
remaining_seconds -= next_duration_seconds | ||
|
||
print(f"🐝 Running {fuzzfn} in {path} for {next_duration_seconds}s before switching to next fuzzer", file=sys.stderr) | ||
run_fuzzer(fuzzfn, path, next_duration_seconds) | ||
print(f"🐝 Completed running {fuzzfn} in {path} for {next_duration_seconds}s. Total remaining time is {remaining_seconds}s", file=sys.stderr) | ||
|
||
def discover_fuzzers(): | ||
fuzzers = {} | ||
for root, dirs, files in os.walk(LIBROOT): | ||
for file in files: | ||
if not file.endswith("test.go"): continue | ||
with open(os.path.join(root, file), "r") as f: | ||
text = f.read() | ||
# ignore multiline comments | ||
text = re.sub(r"(?s)/[*].*?[*]/", "", text) | ||
# ignore single line comments *except* build tags | ||
text = re.sub(r"//.*", "", text) | ||
# Find every function with a name like FuzzXXX | ||
for fuzzfn in re.findall(r"func\s+(Fuzz\w+)", text): | ||
if fuzzfn in fuzzers: | ||
raise Exception(f"Duplicate fuzz function: {fuzzfn}") | ||
fuzzers[fuzzfn] = os.path.relpath(root, LIBROOT) | ||
return fuzzers | ||
|
||
def run_fuzzer(fuzzfn, dir, duration_seconds): | ||
subprocess.check_call(["go", "test", "-run=^$", f"-fuzz=^{fuzzfn}$", f"-fuzztime={duration_seconds}s", f"github.com/smartcontractkit/chainlink/{dir}"], cwd=LIBROOT) | ||
vyzaldysanchez marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if __name__ == "__main__": | ||
main() |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this giant messy script? IMHO we should prefer to use the tooling as directly as possible, and avoid inheriting all this extra noise. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might not need it, but I need to figure out how to keep the results visible, and downloadable, after each run. Which is what the script does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it does a bunch of other junk and prints confusing messages that aren't about fuzzing, because it was just copied from regular tests. I'm not opposed to aligning these scripts, but we need to be deliberate with what we are doing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please tag these with the commit sha like this?
actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1