mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-10-22 06:09:35 +08:00
Preserve the current selector, frozen-input inventory, result codec, coverage collection, runner filtering, and isolated Windows venue work. Component-level regression proofs are recorded in the PR handover. The live classified provider, public execution integration, artifact admission, fresh oracle/shadow evidence, and full platform gates remain incomplete. This checkpoint does not enable or claim verified narrowing. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
342 lines
14 KiB
Python
342 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""Real canonical-runner regression for normal-parent profile write failures.
|
|
|
|
The same fixed oracle applies to every source tree. There is no baseline mode.
|
|
This driver neither changes the collector nor substitutes an LLVM writer.
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
import stat
|
|
import subprocess
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
DEADLINE_SECONDS = 60
|
|
REAP_SECONDS = 5
|
|
LOG_BYTES = 1024 * 1024
|
|
TEST = "gi_exact_file"
|
|
SELECTOR = "gitignore:" + TEST
|
|
ANSI = re.compile(r"\x1b\[[0-9;]*m")
|
|
BAD_SETUP = (
|
|
"Unknown test suite:",
|
|
"No matching test suites requested",
|
|
"selected test not compiled into this build or unknown:",
|
|
"test selection names no test",
|
|
"cannot use coverage directory:",
|
|
"cannot route coverage profiles under:",
|
|
"a coverage run starts with LLVM_PROFILE_FILE=",
|
|
"failed to create isolated test cache",
|
|
)
|
|
|
|
|
|
def identity(path):
|
|
resolved = Path(path).resolve(strict=True)
|
|
info = resolved.stat()
|
|
if not stat.S_ISREG(info.st_mode) or not os.access(str(resolved), os.X_OK):
|
|
raise ValueError("not an executable regular file: " + str(resolved))
|
|
digest = hashlib.sha256()
|
|
with resolved.open("rb") as stream:
|
|
for block in iter(lambda: stream.read(1024 * 1024), b""):
|
|
digest.update(block)
|
|
return {"path": str(resolved), "sha256": digest.hexdigest(), "bytes": info.st_size}
|
|
|
|
|
|
def save_json(path, value):
|
|
with path.open("x", encoding="utf-8") as stream:
|
|
json.dump(value, stream, indent=2, sort_keys=True)
|
|
stream.write("\n")
|
|
|
|
|
|
def drain(pipe, destination, state, stop):
|
|
try:
|
|
with destination.open("xb", buffering=0) as log:
|
|
while True:
|
|
data = pipe.read(4096)
|
|
if not data:
|
|
break
|
|
state["seen_bytes"] += len(data)
|
|
remaining = LOG_BYTES - state["saved_bytes"]
|
|
prefix = memoryview(data)[:max(remaining, 0)]
|
|
while prefix:
|
|
count = log.write(prefix)
|
|
if not count:
|
|
raise OSError("short log write")
|
|
state["saved_bytes"] += count
|
|
prefix = prefix[count:]
|
|
if state["seen_bytes"] > LOG_BYTES:
|
|
state["overflow"] = True
|
|
stop.set()
|
|
except (OSError, ValueError) as exc:
|
|
state["error"] = str(exc)
|
|
stop.set()
|
|
finally:
|
|
pipe.close()
|
|
|
|
|
|
def reap(proc):
|
|
if proc.poll() is not None:
|
|
return None
|
|
try:
|
|
proc.terminate()
|
|
except OSError:
|
|
pass
|
|
try:
|
|
proc.wait(timeout=REAP_SECONDS)
|
|
return None
|
|
except subprocess.TimeoutExpired:
|
|
pass
|
|
try:
|
|
proc.kill()
|
|
proc.wait(timeout=REAP_SECONDS)
|
|
return None
|
|
except (OSError, subprocess.TimeoutExpired):
|
|
return "owned child did not terminate after kill"
|
|
|
|
|
|
def run_process(directory, label, argv, environment):
|
|
record = {"argv": argv, "deadline_seconds": DEADLINE_SECONDS, "returncode": None,
|
|
"failure": None, "stdout": label + ".stdout.log", "stderr": label + ".stderr.log"}
|
|
started = time.monotonic()
|
|
try:
|
|
proc = subprocess.Popen(argv, env=environment, stdin=subprocess.DEVNULL,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0)
|
|
except OSError as exc:
|
|
record["failure"] = "spawn: " + str(exc)
|
|
return record
|
|
record["pid"] = proc.pid
|
|
stop = threading.Event()
|
|
streams = []
|
|
threads = []
|
|
started_pipes = []
|
|
try:
|
|
for kind, pipe in (("stdout", proc.stdout), ("stderr", proc.stderr)):
|
|
state = {"seen_bytes": 0, "saved_bytes": 0, "overflow": False, "error": None}
|
|
thread = threading.Thread(target=drain,
|
|
args=(pipe, directory / record[kind], state, stop), daemon=True)
|
|
thread.start()
|
|
streams.append(state)
|
|
threads.append(thread)
|
|
started_pipes.append(pipe)
|
|
while proc.poll() is None:
|
|
remaining = DEADLINE_SECONDS - (time.monotonic() - started)
|
|
if stop.is_set() or remaining <= 0:
|
|
record["failure"] = "log limit/read failure" if stop.is_set() else "timeout"
|
|
break
|
|
try:
|
|
proc.wait(timeout=min(remaining, 0.1))
|
|
except subprocess.TimeoutExpired:
|
|
pass
|
|
except (OSError, RuntimeError, KeyboardInterrupt) as exc:
|
|
record["failure"] = type(exc).__name__ + ": " + str(exc)
|
|
finally:
|
|
cleanup_error = reap(proc)
|
|
if cleanup_error:
|
|
record["cleanup_error"] = cleanup_error
|
|
record["failure"] = record["failure"] or cleanup_error
|
|
for pipe in (proc.stdout, proc.stderr):
|
|
if pipe not in started_pipes:
|
|
pipe.close()
|
|
join_until = time.monotonic() + REAP_SECONDS
|
|
for thread in threads:
|
|
thread.join(timeout=max(0, join_until - time.monotonic()))
|
|
if any(thread.is_alive() for thread in threads):
|
|
record["failure"] = "pipe reader did not quiesce; unexpected inherited pipe"
|
|
if any(item["overflow"] or item["error"] for item in streams):
|
|
record["failure"] = record["failure"] or "bounded log capture failed"
|
|
record["returncode"] = proc.poll()
|
|
record["elapsed_seconds"] = round(time.monotonic() - started, 3)
|
|
record["streams"] = dict(zip(("stdout", "stderr"), streams))
|
|
return record
|
|
|
|
|
|
def regular_nonempty(path):
|
|
try:
|
|
info = path.lstat()
|
|
return stat.S_ISREG(info.st_mode) and info.st_size > 0
|
|
except OSError:
|
|
return False
|
|
|
|
|
|
def same_path(left, right):
|
|
return os.path.normcase(os.path.normpath(left)) == os.path.normcase(os.path.normpath(str(right)))
|
|
|
|
|
|
def write_error_evidence(stderr, obstacle):
|
|
# Static LLVM22 library evidence; first native RED must confirm this binding.
|
|
# Different wording is an unknown precondition, never guessed to be success.
|
|
errors = [line for line in stderr.splitlines() if line.startswith("LLVM Profile Error:")]
|
|
matched = []
|
|
for line in errors:
|
|
found = re.fullmatch(r'LLVM Profile Error: Failed to write file "(.*)": (.+)', line)
|
|
if not found or not same_path(found.group(1), obstacle):
|
|
return False, errors
|
|
matched.append(line)
|
|
return bool(matched), matched
|
|
|
|
|
|
def test_output(stdout):
|
|
clean = ANSI.sub("", stdout)
|
|
starts = re.findall(r"(?m)^ " + TEST + r"\b", clean)
|
|
rows = re.findall(r"(?m)^ ([A-Za-z_][A-Za-z0-9_]*)[ \t]+(PASS|FAIL|SKIP)\b", clean)
|
|
exact_passes = re.findall(r"(?m)^ " + TEST + r"[ \t]+PASS[ \t]*$", clean)
|
|
summaries = re.findall(
|
|
r"(?m)^[ \t]*(\d+) passed(?:, (\d+) failed)?(?:, (\d+) skipped)?"
|
|
r"(?:, (\d+) deselected)?[ \t]*$", clean)
|
|
return {"started": len(starts), "passed": len(exact_passes), "rows": rows,
|
|
"summaries": [[int(value or 0) for value in row] for row in summaries]}
|
|
|
|
|
|
def expected_test_once(observation):
|
|
return (observation["started"] == 1 and observation["passed"] == 1
|
|
and observation["rows"] == [(TEST, "PASS")])
|
|
|
|
|
|
def merge_profile(case, raw, name, tool, environment):
|
|
output = case / (name + ".profdata")
|
|
result = {"raw": str(raw), "merged": str(output), "valid": False}
|
|
if not regular_nonempty(raw):
|
|
result["failure"] = "raw profile is not a regular nonempty file"
|
|
return result
|
|
command = [tool, "merge", "--instr", "--output=" + str(output), str(raw)]
|
|
result["process"] = run_process(case, "merge-" + name, command, environment)
|
|
process = result["process"]
|
|
result["valid"] = (not process["failure"] and process["returncode"] == 0
|
|
and regular_nonempty(output))
|
|
if not result["valid"]:
|
|
result["failure"] = "matching llvm-profdata did not validate the raw profile"
|
|
return result
|
|
|
|
|
|
def classify(case, mode, process, obstacle):
|
|
answer = {"classification": "infrastructure_error", "reasons": [], "observed_test": None}
|
|
if process["failure"]:
|
|
answer["reasons"].append(process["failure"])
|
|
return answer
|
|
rc = process["returncode"]
|
|
if rc is None or rc < 0 or rc > 255:
|
|
answer["reasons"].append("child did not exit normally")
|
|
return answer
|
|
stdout = (case / process["stdout"]).read_text(encoding="utf-8", errors="replace")
|
|
stderr = (case / process["stderr"]).read_text(encoding="utf-8", errors="replace")
|
|
observed = test_output(stdout)
|
|
answer["observed_test"] = observed
|
|
if any(text in stderr for text in BAD_SETUP):
|
|
answer["reasons"].append("initialization or selection failed")
|
|
return answer
|
|
if observed["started"] > 1 or any(row != (TEST, "PASS") for row in observed["rows"]):
|
|
answer["reasons"].append("unexpected test execution or body failure")
|
|
return answer
|
|
if mode != "begin" and not expected_test_once(observed):
|
|
answer["reasons"].append("selected test did not run exactly once and pass")
|
|
return answer
|
|
if mode == "success":
|
|
if rc or len(observed["summaries"]) != 1 or observed["summaries"][0][:3] != [1, 0, 0]:
|
|
answer["reasons"].append("positive runner control failed")
|
|
elif "LLVM Profile Error:" in stderr or "coverage profile" in stderr.lower():
|
|
answer["reasons"].append("positive control reported a coverage error")
|
|
else:
|
|
answer["classification"] = "pass"
|
|
return answer
|
|
try:
|
|
is_directory = stat.S_ISDIR(obstacle.lstat().st_mode)
|
|
except OSError:
|
|
is_directory = False
|
|
if not is_directory:
|
|
answer["reasons"].append("owned obstacle is no longer a directory")
|
|
return answer
|
|
known_error, evidence = write_error_evidence(stderr, obstacle)
|
|
answer["llvm_write_error_lines"] = evidence
|
|
if not known_error:
|
|
answer["classification"] = "unknown_error_precondition"
|
|
answer["reasons"].append("intended LLVM target-path write failure was not recognized")
|
|
return answer
|
|
if rc == 0:
|
|
answer["classification"] = "regression_failure"
|
|
answer["reasons"].append("coverage write failed but runner silently exited 0")
|
|
elif not any(line.startswith("coverage profile write failed:") for line in stderr.splitlines()):
|
|
answer["reasons"].append("nonzero exit lacks the runner-owned coverage failure diagnostic")
|
|
else:
|
|
answer["classification"] = "pass"
|
|
return answer
|
|
|
|
|
|
def run_case(root, mode, runner, tool):
|
|
case = root / mode
|
|
case.mkdir(mode=0o700)
|
|
coverage = case / "coverage"
|
|
suite = coverage / "gitignore"
|
|
suite.mkdir(mode=0o700, parents=True)
|
|
setup = suite / "_setup.0.profraw"
|
|
parent = suite / (TEST + ".parent.profraw")
|
|
obstacle = setup if mode == "begin" else parent if mode == "end" else None
|
|
if obstacle is not None:
|
|
obstacle.mkdir(mode=0o700)
|
|
environment = os.environ.copy()
|
|
environment["LLVM_PROFILE_FILE"] = "/dev/null"
|
|
environment["CBM_TEST_COVERAGE_DIR"] = str(coverage)
|
|
environment["CBM_TEST_ONLY"] = SELECTOR
|
|
environment.pop("CBM_TEST_ONLY_FILE", None)
|
|
process = run_process(case, "runner", [runner, "gitignore"], environment)
|
|
result = {"case": mode, "runner": process, "coverage_root": str(coverage),
|
|
"obstacle": str(obstacle) if obstacle is not None else None}
|
|
result.update(classify(case, mode, process, obstacle))
|
|
profiles = []
|
|
eligible = result["classification"] in ("pass", "regression_failure")
|
|
if mode in ("success", "end") and eligible:
|
|
profiles.append(merge_profile(case, setup, "setup", tool, environment))
|
|
if mode == "success" and eligible and all(profile["valid"] for profile in profiles):
|
|
profiles.append(merge_profile(case, parent, "parent", tool, environment))
|
|
result["profile_checks"] = profiles
|
|
if any(not profile["valid"] for profile in profiles):
|
|
result["classification"] = "infrastructure_error"
|
|
result["reasons"].append("required success/setup profile could not be validated")
|
|
save_json(case / "result.json", result)
|
|
return result
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--runner", required=True, help="canonical instrumented test-runner")
|
|
parser.add_argument("--llvm-profdata", required=True, help="matching LLVM tool")
|
|
parser.add_argument("--output-dir", required=True, help="new owned evidence directory")
|
|
args = parser.parse_args()
|
|
try:
|
|
runner = identity(args.runner)
|
|
tool = identity(args.llvm_profdata)
|
|
root = Path(args.output_dir).resolve()
|
|
root.mkdir(mode=0o700)
|
|
except (OSError, ValueError) as exc:
|
|
print("INFRASTRUCTURE ERROR: " + str(exc), file=sys.stderr)
|
|
return 2
|
|
receipt = {"schema": 1, "fixed_oracle": True, "selector": SELECTOR,
|
|
"deadline_seconds": DEADLINE_SECONDS, "reap_grace_seconds": REAP_SECONDS,
|
|
"log_bytes_per_stream": LOG_BYTES, "runner": runner, "llvm_profdata": tool,
|
|
"cwd": str(Path.cwd()), "cases": [], "overall": "infrastructure_error"}
|
|
try:
|
|
for mode in ("success", "begin", "end"):
|
|
result = run_case(root, mode, runner["path"], tool["path"])
|
|
receipt["cases"].append(result)
|
|
print(mode + ": " + result["classification"])
|
|
if result["classification"] in ("infrastructure_error", "unknown_error_precondition"):
|
|
break
|
|
unchanged = identity(runner["path"]) == runner and identity(tool["path"]) == tool
|
|
if not unchanged:
|
|
receipt["error"] = "runner or llvm-profdata changed during proof"
|
|
classes = [case["classification"] for case in receipt["cases"]]
|
|
if unchanged and len(classes) == 3 and all(item in ("pass", "regression_failure") for item in classes):
|
|
receipt["overall"] = "regression_failure" if "regression_failure" in classes else "pass"
|
|
except (OSError, ValueError, KeyboardInterrupt) as exc:
|
|
receipt["error"] = type(exc).__name__ + ": " + str(exc)
|
|
finally:
|
|
save_json(root / "result.json", receipt)
|
|
return {"pass": 0, "regression_failure": 1, "infrastructure_error": 2}[receipt["overall"]]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|