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
15 changes: 13 additions & 2 deletions .github/workflows/gc-native-roots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -535,15 +535,26 @@ jobs:
# Same answer as the shadow stack, and a collection that actually
# moved something. Without the movement assert this passes with the
# conservative scan doing all the rooting (#7336, #7338).
#
# PERRY_GC_DIAG=1 is what makes the evacuation assert able to see
# anything: `[gc-copy-minor] ran copied_objects=...` is printed only
# under that variable (`gc/copying.rs`). Without it the trace holds
# nothing but the probe's own `#gcmetric` lines, the assert reads
# 0 copying minors / 0 objects copied off an empty file, and the step
# fails no matter how the collector behaved — which is how this arm
# read on its first-ever execution (three of four arms in this matrix
# were permanently queued until #7393). Diagnostics go to stderr only,
# so the control diff below is unaffected.
./target/perry-dev/perry "$probe" -o /tmp/inproc-09-control
PERRY_GC_HEAP_LIMIT=8 PERRY_GC_INCREMENTAL=0 PERRY_CONSERVATIVE_STACK_SCAN=off \
/tmp/inproc-09-control > /tmp/inproc-09.control.out 2>/dev/null
PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1 \
PERRY_GC_DIAG=1 PERRY_GC_FORCE_EVACUATE=1 PERRY_GC_VERIFY_EVACUATION=1 \
PERRY_GC_HEAP_LIMIT=8 PERRY_GC_INCREMENTAL=0 PERRY_CONSERVATIVE_STACK_SCAN=off \
/tmp/inproc-09 > /tmp/inproc-09.out 2> /tmp/inproc-09.err
diff /tmp/inproc-09.control.out /tmp/inproc-09.out \
|| { echo "::error::in-process RS4GC diverged from the shadow-stack control"; exit 1; }
python3 scripts/gc_evacuation_liveness_assert.py /tmp/inproc-09.err
python3 scripts/gc_evacuation_liveness_assert.py /tmp/inproc-09.err \
--probe "09_try_catch_roots (in-process)"

# And it must be RS4GC doing the lowering, not a per-function bail to
# the bridge -- which would make this arm green while testing the
Expand Down
12 changes: 12 additions & 0 deletions changelog.d/7414-macos-rs4gc-inprocess-gc-diag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
**Fixed** the macOS `native-roots-rs4gc` arm's in-process step could never pass.

It asserts that a copying minor moved objects by counting
`[gc-copy-minor] ran copied_objects=` lines, but both prints are gated on
`PERRY_GC_DIAG` and the step never set it. The trace held only the probe's own
`#gcmetric` lines, so the assert read 0/0 off an effectively empty file and
failed regardless of collector behaviour — the inverse of a gate that cannot
fail. It shipped with the step in #7339 and had never run, because three of four
arms in this matrix were permanently queued until #7393.

The liveness assert now distinguishes "no collector diagnostics at all" from
"the collector moved nothing", which the old message conflated.
15 changes: 15 additions & 0 deletions scripts/gc_evacuation_liveness_assert.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
COPIED = re.compile(r"copied_objects=(\d+)")
ELIGIBLE = re.compile(r"\[gc-copy-minor\] eligible=(\w+)(?: fallback=(\S+))?")
MANUAL = re.compile(r"\[gc-scan-fallback\] site=manual_collect")
# Any collector diagnostic at all. Every one of them is printed behind
# `PERRY_GC_DIAG`, so a trace with none of them was produced by a run that did
# not set it — which is indistinguishable, by counts alone, from a collector
# that moved nothing. Told apart below, because guessing wrong costs a build:
# the `gc-native-roots` in-process arm read as "evacuated NOTHING" on its
# first-ever execution purely for want of the variable.
ANY_DIAG = re.compile(r"^\[gc-[a-z-]+\]", re.MULTILINE)


def main() -> int:
Expand All @@ -44,6 +51,14 @@ def main() -> int:
print(f"{args.probe}: evacuation live — {ran} copying minor(s), {copied} objects copied")
return 0

if not ANY_DIAG.search(text):
print(f"::error::{args.probe}: {args.trace} carries no collector diagnostics at all, "
"so this assert measured nothing about the collector. Every line it reads is "
"printed behind PERRY_GC_DIAG; re-run the binary with PERRY_GC_DIAG=1 set "
"alongside PERRY_GC_FORCE_EVACUATE=1. (Diagnostics go to stderr, so this does "
"not disturb an stdout oracle diff.)")
return 1

print(f"::error::{args.probe}: the forced-evacuation arm evacuated NOTHING "
f"({ran} copying minors, {copied} objects copied). The arm is vacuous: "
f"it proves the program ran, not that a moving collector did (#7336).")
Expand Down
Loading