Browse Source

tests: nonvacuity restores Compiler.mod when the run is interrupted

The EXIT trap restored Runtime.mod, Exec86.mod, Shell.mod, comimage.py and
comtest.py, but not Compiler.mod -- the file mutated by the most cases by far.
Its copy was taken at the BP displacement section, halfway down the file and
well AFTER `trap restore_all EXIT' was set, so an interrupted run left
Compiler.mod half-mutated, with `git status' showing a source no case had
finished with.  SAVED_C now lives at the top beside every other copy, and
restore_all puts it back -- which is what the comment there already promised.

Fixing that exposed that the promise was false twice over: an unhandled signal
does not run an EXIT trap at all.  bash 5.2 dies on the spot, so Runtime.mod
and the rest were never protected against interruption either, only against an
`exit' inside the script.  Added handlers that only `exit', because `exit' from
a trap DOES fire the EXIT trap -- and it has to exit, or the run would go on
using a tree it has just put back.

Measured on bash 5.2, not assumed.  TERM and HUP fire them and restore
everything.  INT is trapped but usually never reaches the script: a foreground
child that dies of SIGINT makes bash treat the signal as the CHILD's and
continue the script instead, so Ctrl-C does not abort this run at all and
SIGTERM is what actually stops it.  Said so in the comment rather than implied.

Verified by interrupting a live run at the moment Compiler.mod is mutated:
before this, it was left byte-different from the pre-run copy; now it comes
back byte-identical.  The full run is unchanged -- non-vacuity 78 ok 0 failed,
compile matrix 56/56.
Eric Streit 22 hours ago
parent
commit
ff7b2ddc17
1 changed files with 30 additions and 2 deletions
  1. 30 2
      shell/tests/nonvacuity.sh

+ 30 - 2
shell/tests/nonvacuity.sh

@@ -126,17 +126,26 @@ DUMP=../tmp/nonvacuity.dump
 # both are mutated by cases further down, and neither is recoverable from git
 # once the mutation is staged over - so they join the trap here for the same
 # reason, and an interrupted run cannot leave the layout half-written.
+#
+# Compiler.mod is the file mutated by the most cases, and it used to be saved
+# BESIDE the first case that needed it - the BP displacement section halfway
+# down this file, which is well AFTER this trap is set.  An interrupted run
+# therefore left Compiler.mod half-mutated, with `git status` showing a source
+# that no case had finished with.  It joins the copies above, taken before the
+# first mutation and covered by the same trap.
 mkdir -p ../tmp
 SAVED_E=../tmp/nonvacuity.Exec86.mod
 SAVED_SH=../tmp/nonvacuity.Shell.mod
 SAVED_CI=../tmp/nonvacuity.comimage.py
 SAVED_T=../tmp/nonvacuity.comtest.py
+SAVED_C=../tmp/nonvacuity.Compiler.mod
 
 cp Runtime.mod "$SAVED" || exit 1
 cp Exec86.mod "$SAVED_E" || exit 1
 cp Shell.mod "$SAVED_SH" || exit 1
 cp tests/comimage.py "$SAVED_CI" || exit 1
 cp tests/comtest.py "$SAVED_T" || exit 1
+cp Compiler.mod "$SAVED_C" || exit 1
 
 restore_all () {
     cp "$SAVED" Runtime.mod
@@ -144,13 +153,32 @@ restore_all () {
     cp "$SAVED_SH" Shell.mod
     cp "$SAVED_CI" tests/comimage.py
     cp "$SAVED_T" tests/comtest.py
+    cp "$SAVED_C" Compiler.mod
     "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
     "$GM2" -fiso -c Exec86.mod >/dev/null 2>&1
     # The shell is rebuilt as well: a test that runs against a binary built
     # from a half-restored tree is reporting on the mutation, not on the code.
+    # make rebuilds Compiler.o out of the Compiler.mod restored just above.
     make >/dev/null 2>&1
 }
 trap restore_all EXIT
+#
+# An unhandled signal does NOT run an EXIT trap: bash dies on the spot.  That is
+# what the comment above promises ("an interrupted run cannot leave the layout
+# half-written") and what this file did not actually do.  A handler that only
+# `exit's is enough, because `exit' from a trap DOES fire the EXIT trap -- and
+# it has to exit, or the run would go on using a tree it has just put back.
+#
+# Measured on bash 5.2: TERM and HUP fire these and restore everything, so
+# `kill' and a session teardown are covered.  INT is trapped too, but in the
+# usual case it never reaches this script: a foreground child that dies of
+# SIGINT makes bash treat the signal as the CHILD's and continue the script
+# instead.  So Ctrl-C does not abort this run at all -- it kills the current
+# case's child and the next case starts -- and SIGTERM is what actually stops
+# it.  129/130/143 = 128+SIGHUP/INT/TERM.
+trap 'exit 129' HUP
+trap 'exit 130' INT
+trap 'exit 143' TERM
 
 pass=0
 fail=0
@@ -355,8 +383,8 @@ echo
 echo "== the BP displacement rule (check_framedisp.py)"
 # This one is about Compiler.mod rather than the runtime, and it needs the
 # whole toolchain rebuilt (comtest, not rtprobe), so it gets its own rebuild.
-SAVED_C=../tmp/nonvacuity.Compiler.mod
-cp Compiler.mod "$SAVED_C" || exit 1
+# SAVED_C itself is taken at the top of this file, beside the EXIT trap, so an
+# interrupted run restores Compiler.mod as well.
 
 rebuild_compiler () {
     $GM2 -fiso -c Compiler.mod >/dev/null 2>&1 || return 1