Lane CR named all 27 template hooks off, passed check_shim_configs.py, and the shim installed six detours: the M0 asm stub and the FPU-force module's four sampling detours, on by default under keys that do not start with hook. The checker was exhaustive over the wrong list. It now requires fpu.sample_turn and fpu.sample_ticks off in an exhaustive config and names the M0 stub in its OK line. All five exhaustive configs amended; their past measurements stand because each was proved byte-neutral against a control.
132 lines
5.9 KiB
Python
132 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Structural checks on the shim.cfg.* templates.
|
|
|
|
Lane BP found two defects in `shim.cfg.cbpin` while copying it for a determinism
|
|
probe, and its own first draft reproduced one of them -- which is what a trap looks
|
|
like. Both are checked here:
|
|
|
|
* A **wrapped header comment landing on a live setting.** `cbpin`'s first line ran
|
|
on past the comment and left `aiseed.values=...\\u0060.` as a real setting ahead of the
|
|
real one at the bottom, so the file contradicted its own "filled in per run" note.
|
|
* A **key set twice.** The last one silently wins, which is exactly how a stale
|
|
value hides in a config nobody re-reads.
|
|
|
|
There is deliberately **no global check that a template names every registered
|
|
hook**. `hooks=trace` means "trace everything except what is turned off", and most
|
|
templates rely on that on purpose -- an exhaustiveness rule applied to all of them
|
|
would be a rule about a style nobody agreed to. It matters only where a lane wants
|
|
a *minimal* hook set, and no file states that intent, so a template opts in by
|
|
carrying a line reading exactly `# exhaustive` in its header. For those, an unnamed
|
|
hook is a detour the config did not ask for, and on a determinism probe an extra
|
|
detour is precisely what rule 19 says can move the bytes.
|
|
|
|
Exits non-zero with the offending file and what is wrong with it.
|
|
"""
|
|
import pathlib
|
|
import re
|
|
import sys
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
SRC = ROOT / "src" / "shim"
|
|
|
|
# A hook declares itself with `static constexpr const char* name = "...";`
|
|
NAME_RE = re.compile(r'static\s+constexpr\s+const\s+char\*\s+name\s*=\s*"([^"]+)"')
|
|
HOOK_RE = re.compile(r"^hook\.([^=]+)=")
|
|
|
|
# Not real hooks: the doc-comment's example in trace/hook.h, and the self-test hooks,
|
|
# which exist to make the harness fail on purpose and are never part of a measurement.
|
|
EXEMPT = {"Game::Foo"}
|
|
EXEMPT_PREFIXES = ("Shim::SelfTest::",)
|
|
|
|
# Detours that are installed by default and are NOT template hooks. The FPU-force module's
|
|
# sampling keys accept exactly `on`/`off`. The M0 asm stub has no key at all and is always
|
|
# installed when `hooks != off`; it is mentioned in the OK line so nobody reads "27" as "all".
|
|
ALWAYS_ON_UNLESS_OFF = ("fpu.sample_turn", "fpu.sample_ticks")
|
|
UNNAMEABLE_DETOURS = ("Mars::Application::Initialize (M0 asm stub, always on unless hooks=off)",)
|
|
|
|
# Settings whose values legitimately end in a period or a path separator.
|
|
PROSE_SAFE = ("path", "out", "dir", "file")
|
|
|
|
|
|
def registered_hooks():
|
|
names = set()
|
|
for path in SRC.rglob("*"):
|
|
if path.suffix in (".h", ".cpp"):
|
|
for m in NAME_RE.finditer(path.read_text(encoding="utf-8", errors="replace")):
|
|
names.add(m.group(1))
|
|
return {n for n in names if n not in EXEMPT and not n.startswith(EXEMPT_PREFIXES)}
|
|
|
|
|
|
def check(cfg, hooks):
|
|
problems = []
|
|
lines = cfg.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
settings = [l for l in lines if l.strip() and not l.lstrip().startswith("#")]
|
|
|
|
for l in settings:
|
|
key = l.split("=", 1)[0].strip()
|
|
if "=" not in l:
|
|
problems.append(f"line is neither a comment nor a setting: {l!r}")
|
|
elif l.rstrip().endswith(("`", "`.")) or (
|
|
l.rstrip().endswith(".") and not any(p in key.lower() for p in PROSE_SAFE)
|
|
):
|
|
problems.append(f"setting looks like a wrapped comment: {l!r}")
|
|
|
|
seen = {}
|
|
for l in settings:
|
|
key = l.split("=", 1)[0].strip()
|
|
if key in seen:
|
|
problems.append(f"{key!r} set twice ({seen[key]!r} then {l!r})")
|
|
seen[key] = l
|
|
|
|
if any(l.strip() == "# exhaustive" for l in lines):
|
|
if not any(l.strip() == "hooks=trace" for l in settings):
|
|
problems.append("declares `# exhaustive` but does not set `hooks=trace`, so it does nothing")
|
|
named = {m.group(1) for m in (HOOK_RE.match(l) for l in settings) if m}
|
|
missing = sorted(hooks - named)
|
|
if missing:
|
|
problems.append(
|
|
f"declares `# exhaustive` but does not name {len(missing)} registered hook(s), "
|
|
"which therefore default ON: " + ", ".join(missing)
|
|
)
|
|
# Template hooks are not the only detours. Lane CR named all 27, passed this check,
|
|
# and the shim installed SIX: the M0 Application::Initialize asm stub (unconditional
|
|
# whenever hooks != off -- it cannot be named off, so it is reported, not required)
|
|
# and the FPU-force module's four sampling detours, which are ON BY DEFAULT and are
|
|
# controlled by keys that do not start with `hook.`. An exhaustive config must turn
|
|
# those off explicitly, or "exhaustive" is a claim about the wrong list.
|
|
kv = {l.split("=", 1)[0].strip(): l.split("=", 1)[1].strip() for l in settings if "=" in l}
|
|
for key in ALWAYS_ON_UNLESS_OFF:
|
|
if kv.get(key) != "off":
|
|
problems.append(
|
|
f"declares `# exhaustive` but `{key}` is not `off` (it defaults ON and installs "
|
|
"detours no `hook.` key names)"
|
|
)
|
|
return problems
|
|
|
|
|
|
def main():
|
|
hooks = registered_hooks()
|
|
if not hooks:
|
|
print("check_shim_configs: found no registered hooks -- the parser is wrong, not the configs")
|
|
return 1
|
|
|
|
failures = []
|
|
exhaustive = 0
|
|
for cfg in sorted(SRC.glob("shim.cfg.*")):
|
|
if any(l.strip() == "# exhaustive" for l in cfg.read_text(errors="replace").splitlines()):
|
|
exhaustive += 1
|
|
for p in check(cfg, hooks):
|
|
failures.append(f"{cfg.name}: {p}")
|
|
|
|
if failures:
|
|
print("check_shim_configs: FAILED")
|
|
for f in failures:
|
|
print(" " + f)
|
|
return 1
|
|
print(f"check_shim_configs: OK ({len(hooks)} template hooks + {len(ALWAYS_ON_UNLESS_OFF)} default-on "
|
|
f"FPU sampling keys + {len(UNNAMEABLE_DETOURS)} unnameable detour; {exhaustive} template(s) declared exhaustive)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|