Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions ci/bench-icount.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,18 @@ function run_icount_benchmarks() {
shift
done

# Run iai-callgrind benchmarks
cargo bench "${cargo_args[@]}" -- "${iai_args[@]}"
# Run iai-callgrind benchmarks. Do this in a subshell with `&& true` to
# capture rather than exit on error.
(cargo bench "${cargo_args[@]}" -- "${iai_args[@]}") && true
exit_code="$?"

# NB: iai-callgrind should exit on error but does not, so we inspect the sumary
# for errors. See https://github.com/iai-callgrind/iai-callgrind/issues/337
if [ -n "${PR_NUMBER:-}" ]; then
# If this is for a pull request, ignore regressions if specified.
./ci/ci-util.py check-regressions --home "$iai_home" --allow-pr-override "$PR_NUMBER"
else
if [ "$exit_code" -eq 0 ]; then
echo "Benchmarks completed with no regressions"
elif [ -z "${PR_NUMBER:-}" ]; then
# Disregard regressions after merge
./ci/ci-util.py check-regressions --home "$iai_home" || true
echo "Benchmarks completed with regressions; ignoring (not in a PR)"
else
./ci/ci-util.py handle-banch-regressions "$PR_NUMBER"
fi
}

Expand Down
84 changes: 19 additions & 65 deletions ci/ci-util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import subprocess as sp
import sys
from dataclasses import dataclass
from glob import glob, iglob
from glob import glob
from inspect import cleandoc
from os import getenv
from pathlib import Path
Expand All @@ -38,14 +38,10 @@

Note that `--extract` will overwrite files in `iai-home`.

check-regressions [--home iai-home] [--allow-pr-override pr_number]
Check `iai-home` (or `iai-home` if unspecified) for `summary.json`
files and see if there are any regressions. This is used as a workaround
for `iai-callgrind` not exiting with error status; see
<https://github.com/iai-callgrind/iai-callgrind/issues/337>.

If `--allow-pr-override` is specified, the regression check will not exit
with failure if any line in the PR starts with `allow-regressions`.
handle-bench-regressions PR_NUMBER
Exit with success if the pull request contains a line starting with
`ci: allow-regressions`, indicating that regressions in benchmarks should
be accepted. Otherwise, exit 1.
"""
)

Expand Down Expand Up @@ -365,64 +361,22 @@ def locate_baseline(flags: list[str]) -> None:
eprint("baseline extracted successfully")


def check_iai_regressions(args: list[str]):
"""Find regressions in iai summary.json files, exit with failure if any are
found.
"""

iai_home_str = "iai-home"
pr_number = None

while len(args) > 0:
match args:
case ["--home", home, *rest]:
iai_home_str = home
args = rest
case ["--allow-pr-override", pr_num, *rest]:
pr_number = pr_num
args = rest
case _:
eprint(USAGE)
exit(1)

iai_home = Path(iai_home_str)

found_summaries = False
regressions: list[dict] = []
for summary_path in iglob("**/summary.json", root_dir=iai_home, recursive=True):
found_summaries = True
with open(iai_home / summary_path, "r") as f:
summary = json.load(f)

summary_regs = []
run = summary["callgrind_summary"]["callgrind_run"]
fname = summary["function_name"]
id = summary["id"]
name_entry = {"name": f"{fname}.{id}"}

for segment in run["segments"]:
summary_regs.extend(segment["regressions"])
def handle_bench_regressions(args: list[str]):
"""Exit with error unless the PR message contains an ignore directive."""

summary_regs.extend(run["total"]["regressions"])

regressions.extend(name_entry | reg for reg in summary_regs)

if not found_summaries:
eprint(f"did not find any summary.json files within {iai_home}")
exit(1)
match args:
case [pr_number]:
pr_number = pr_number
case _:
eprint(USAGE)
exit(1)

if len(regressions) == 0:
eprint("No regressions found")
pr = PrInfo.load(pr_number)
if pr.contains_directive(REGRESSION_DIRECTIVE):
eprint("PR allows regressions")
return

eprint("Found regressions:", json.dumps(regressions, indent=4))

if pr_number is not None:
pr = PrInfo.load(pr_number)
if pr.contains_directive(REGRESSION_DIRECTIVE):
eprint("PR allows regressions, returning")
return

eprint("Regressions were found; benchmark failed")
exit(1)


Expand All @@ -433,8 +387,8 @@ def main():
ctx.emit_workflow_output()
case ["locate-baseline", *flags]:
locate_baseline(flags)
case ["check-regressions", *args]:
check_iai_regressions(args)
case ["handle-bench-regressions", *args]:
handle_bench_regressions(args)
case ["--help" | "-h"]:
print(USAGE)
exit()
Expand Down
Loading