update
This commit is contained in:
Executable
+360
@@ -0,0 +1,360 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${PRE15_DUT:?PRE15_DUT is required}"
|
||||
: "${PRE15_ROOT:?PRE15_ROOT is required}"
|
||||
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
|
||||
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
|
||||
. "$PRE15_LIB_DIR/runner.sh"
|
||||
|
||||
gate=$PRE15_DUT/tests/pre15/gates/P15-006.sh
|
||||
input=$PRE15_DUT/tests/pre15/gates/P15-006-input.json
|
||||
oracle=$PRE15_DUT/tests/pre15/fixtures/B07-map-oracle.json
|
||||
artifacts=$PRE15_RUN_DIR/artifacts
|
||||
baseline=6673f51152a5195a8a8903aa801f820abce7936e
|
||||
b07a=141b11f0d847a63a47b6c6143e4c2913a1147a0f
|
||||
|
||||
for tool in cc git python3 sha256sum; do
|
||||
command -v "$tool" >/dev/null 2>&1 || \
|
||||
pre15_infra_blocked "missing B07b host tool: $tool"
|
||||
done
|
||||
|
||||
pre15_record_fixture b07b-gate "$gate"
|
||||
pre15_record_fixture b07b-input "$input"
|
||||
pre15_record_fixture b07b-oracle "$oracle"
|
||||
for source in internal.h data.c zmap.c zdata.c super.c; do
|
||||
pre15_record_fixture "b07b-$source" "$PRE15_DUT/src/$source"
|
||||
done
|
||||
|
||||
mkdir -p "$artifacts"
|
||||
pre15_target_reached
|
||||
|
||||
if "$gate" --base "$baseline" --oracle "$oracle" \
|
||||
--output "$artifacts/baseline" >"$artifacts/baseline.stdout" \
|
||||
2>"$artifacts/baseline.stderr"; then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B07b frozen P15-006 baseline replay failed'
|
||||
fi
|
||||
|
||||
if python3 - "$gate" "$artifacts/P15-006-B07b.py" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
source = Path(sys.argv[1]).read_text(encoding="utf-8")
|
||||
marker = "<<'PY'\n"
|
||||
start = source.index(marker) + len(marker)
|
||||
end = source.rindex("\nPY\n")
|
||||
program = source[start:end]
|
||||
|
||||
|
||||
def replace_once(old: str, new: str) -> None:
|
||||
global program
|
||||
if program.count(old) != 1:
|
||||
raise SystemExit(f"P15-006 derivation marker count changed: {old[:60]!r}")
|
||||
program = program.replace(old, new)
|
||||
|
||||
|
||||
replace_once(
|
||||
''' if actual_hash != expected_hash:
|
||||
raise SystemExit(f"protected producer/GEOM body changed: {key}")
|
||||
''',
|
||||
''' if actual_hash != expected_hash and key != "data.c:erofs_map_blocks_chunk":
|
||||
raise SystemExit(f"protected producer/GEOM body changed: {key}")
|
||||
''',
|
||||
)
|
||||
replace_once(
|
||||
'''if candidate_mode:
|
||||
legacy_body = extract_function(data_source, "erofs_map_blocks_legacy")
|
||||
expected_legacy_hash = SPEC["candidate_legacy_map_sha256"]
|
||||
else:
|
||||
legacy_body = extract_function(data_source, "erofs_map_blocks")
|
||||
expected_legacy_hash = SPEC["baseline_legacy_map_sha256"]
|
||||
if sha256_bytes(legacy_body.encode("utf-8")) != expected_legacy_hash:
|
||||
raise SystemExit("plain/chunk legacy producer body changed")
|
||||
''',
|
||||
'''if candidate_mode:
|
||||
legacy_body = extract_function(data_source, "erofs_map_blocks_legacy")
|
||||
expected_legacy_hash = None
|
||||
else:
|
||||
legacy_body = extract_function(data_source, "erofs_map_blocks")
|
||||
expected_legacy_hash = SPEC["baseline_legacy_map_sha256"]
|
||||
if expected_legacy_hash is not None and sha256_bytes(legacy_body.encode("utf-8")) != expected_legacy_hash:
|
||||
raise SystemExit("plain/chunk legacy producer body changed")
|
||||
''',
|
||||
)
|
||||
replace_once(
|
||||
'''program += extract_function(data_source, "erofs_inline_tail_start")
|
||||
program += extract_function(data_source, "erofs_map_blocks_chunk")
|
||||
program += legacy_body
|
||||
if candidate_mode:
|
||||
program += extract_function(data_source, "erofs_map_blocks")
|
||||
''',
|
||||
'''program += extract_function(data_source, "erofs_inline_tail_start")
|
||||
program += extract_function(data_source, "erofs_map_blocks_chunk")
|
||||
if candidate_mode:
|
||||
program += extract_function(data_source, "erofs_map_blocks_flatmode")
|
||||
program += extract_function(data_source, "erofs_map_blocks")
|
||||
else:
|
||||
program += legacy_body
|
||||
''',
|
||||
)
|
||||
replace_once(
|
||||
''' adapter_program += extract_function(data_source, "erofs_inline_tail_start")
|
||||
adapter_program += extract_function(data_source, "erofs_map_blocks_chunk")
|
||||
adapter_program += legacy_body
|
||||
adapter_program += extract_function(data_source, "erofs_map_blocks")
|
||||
''',
|
||||
''' adapter_program += extract_function(data_source, "erofs_inline_tail_start")
|
||||
adapter_program += extract_function(data_source, "erofs_map_blocks_chunk")
|
||||
adapter_program += extract_function(data_source, "erofs_map_blocks_flatmode")
|
||||
adapter_program += extract_function(data_source, "erofs_map_blocks")
|
||||
''',
|
||||
)
|
||||
|
||||
Path(sys.argv[2]).write_text(program + "\n", encoding="ascii")
|
||||
PY
|
||||
then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B07b could not derive the current-producer replay'
|
||||
fi
|
||||
|
||||
if python3 "$artifacts/P15-006-B07b.py" "$PRE15_ROOT" "$input" \
|
||||
worktree '' "$artifacts/candidate" "$oracle" \
|
||||
>"$artifacts/candidate.stdout" 2>"$artifacts/candidate.stderr"; then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B07b object producer replay failed'
|
||||
fi
|
||||
|
||||
if python3 - "$PRE15_ROOT" "$PRE15_DUT" "$oracle" \
|
||||
"$artifacts/baseline" "$artifacts/candidate" "$artifacts/producer-check.json" \
|
||||
"$b07a" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
root = Path(sys.argv[1])
|
||||
dut = Path(sys.argv[2])
|
||||
oracle = json.loads(Path(sys.argv[3]).read_text(encoding="ascii"))
|
||||
baseline = Path(sys.argv[4])
|
||||
candidate = Path(sys.argv[5])
|
||||
output = Path(sys.argv[6])
|
||||
b07a = sys.argv[7]
|
||||
|
||||
|
||||
def load(directory: Path, name: str) -> dict:
|
||||
return json.loads((directory / name).read_text(encoding="ascii"))
|
||||
|
||||
|
||||
def extract_function(source: str, name: str) -> str:
|
||||
match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
|
||||
if not match:
|
||||
raise SystemExit(f"missing function: {name}")
|
||||
start = source.rfind("\n\n", 0, match.start()) + 2
|
||||
brace = source.find("{", match.end())
|
||||
depth = 0
|
||||
state = "code"
|
||||
index = brace
|
||||
while index < len(source):
|
||||
char = source[index]
|
||||
following = source[index + 1] if index + 1 < len(source) else ""
|
||||
if state == "code":
|
||||
if char == "/" and following == "*":
|
||||
state = "block"
|
||||
index += 2
|
||||
continue
|
||||
if char == "/" and following == "/":
|
||||
state = "line"
|
||||
index += 2
|
||||
continue
|
||||
if char == '"':
|
||||
state = "string"
|
||||
elif char == "'":
|
||||
state = "character"
|
||||
elif char == "{":
|
||||
depth += 1
|
||||
elif char == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return source[start : index + 1]
|
||||
elif state == "block" and char == "*" and following == "/":
|
||||
state = "code"
|
||||
index += 2
|
||||
continue
|
||||
elif state == "line" and char == "\n":
|
||||
state = "code"
|
||||
elif state in {"string", "character"}:
|
||||
if char == "\\":
|
||||
index += 2
|
||||
continue
|
||||
if (state == "string" and char == '"') or (
|
||||
state == "character" and char == "'"
|
||||
):
|
||||
state = "code"
|
||||
index += 1
|
||||
raise SystemExit(f"unterminated function: {name}")
|
||||
|
||||
|
||||
def committed_source(path: str) -> str:
|
||||
completed = subprocess.run(
|
||||
["git", "-C", str(root), "show", f"{b07a}:repo-pre-15/src/{path}"],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
raise SystemExit(f"cannot read B07a source {path}: {completed.stderr}")
|
||||
return completed.stdout
|
||||
|
||||
|
||||
base_result = load(baseline, "result.json")
|
||||
base_tuples = load(baseline, "tuples.json")
|
||||
base_devices = load(baseline, "devices.json")
|
||||
result = load(candidate, "result.json")
|
||||
tuples = load(candidate, "tuples.json")
|
||||
devices = load(candidate, "devices.json")
|
||||
|
||||
for label, record in (("baseline", base_result), ("candidate", result)):
|
||||
if record["status"] != "GO" or record["failures"]:
|
||||
raise SystemExit(f"{label} P15-006 replay did not GO: {record!r}")
|
||||
if base_result["map_case_count"] != 80 or result["map_case_count"] != 80:
|
||||
raise SystemExit("B07 map denominator changed")
|
||||
if base_result["device_case_count"] != 13 or result["device_case_count"] != 13:
|
||||
raise SystemExit("B07 device denominator changed")
|
||||
if result["oracle_equal"] is not True or base_result["oracle_equal"] is not True:
|
||||
raise SystemExit("frozen B07 oracle comparison failed")
|
||||
if result["model_sha256"] != oracle["model_sha256"]:
|
||||
raise SystemExit("independent P15-006 model digest changed")
|
||||
if result["tuple_bytes_sha256"] != oracle["tuple_bytes_sha256"]:
|
||||
raise SystemExit("full tuple stream differs from the frozen B07 oracle")
|
||||
if result["adapter_probe"]["status"] != "PASS":
|
||||
raise SystemExit("common adapter probe failed after producer migration")
|
||||
|
||||
base_records = {record["id"]: record for record in base_tuples["records"]}
|
||||
records = {record["id"]: record for record in tuples["records"]}
|
||||
if set(base_records) != set(records) or len(records) != 80:
|
||||
raise SystemExit("B07 tuple ID set changed")
|
||||
full_diffs = [
|
||||
case_id for case_id in records
|
||||
if records[case_id]["tuple_hex"] != base_records[case_id]["tuple_hex"]
|
||||
]
|
||||
producer_ids = [
|
||||
record["id"] for record in tuples["records"] if record["engine"] == "data"
|
||||
]
|
||||
if len(producer_ids) != 50:
|
||||
raise SystemExit(f"plain/chunk producer denominator changed: {len(producer_ids)}")
|
||||
producer_diffs = [case_id for case_id in producer_ids if case_id in full_diffs]
|
||||
producer_bytes = b"".join(bytes.fromhex(records[case_id]["tuple_hex"]) for case_id in producer_ids)
|
||||
producer_sha256 = hashlib.sha256(producer_bytes).hexdigest()
|
||||
|
||||
if full_diffs or producer_diffs:
|
||||
raise SystemExit(
|
||||
f"B07b tuple difference: producer={producer_diffs!r} full={full_diffs!r}"
|
||||
)
|
||||
if (
|
||||
devices["records"] != base_devices["records"]
|
||||
or devices["status"] != "PASS"
|
||||
or base_devices["status"] != "PASS"
|
||||
):
|
||||
raise SystemExit("B07b device-resolution records changed")
|
||||
|
||||
data = (dut / "src/data.c").read_text(encoding="utf-8")
|
||||
chunk = extract_function(data, "erofs_map_blocks_chunk")
|
||||
flatmode = extract_function(data, "erofs_map_blocks_flatmode")
|
||||
common = extract_function(data, "erofs_map_blocks")
|
||||
legacy = extract_function(data, "erofs_map_blocks_legacy")
|
||||
if "struct erofs_map_blocks *map" not in chunk.split("{", 1)[0]:
|
||||
raise SystemExit("chunk producer does not accept the common map object")
|
||||
for marker in (
|
||||
"map->m_pa", "map->m_llen", "map->m_plen", "map->m_deviceid",
|
||||
"map->m_flags |= EROFS_MAP_MAPPED",
|
||||
):
|
||||
if marker not in chunk:
|
||||
raise SystemExit(f"chunk producer field is not object-backed: {marker}")
|
||||
for marker in (
|
||||
"map->m_la", "map->m_pa", "map->m_llen", "map->m_plen",
|
||||
"EROFS_MAP_MAPPED", "EROFS_MAP_META",
|
||||
):
|
||||
if marker not in flatmode:
|
||||
raise SystemExit(f"flat producer field is not object-backed: {marker}")
|
||||
if "erofs_map_blocks_flatmode(sbi, vi, &next)" not in common:
|
||||
raise SystemExit("common entry does not call the object flat producer")
|
||||
if "erofs_map_blocks(sbi, vi, &map)" not in legacy:
|
||||
raise SystemExit("legacy scatter adapter does not delegate to the object entry")
|
||||
if len(re.findall(r"\berofs_map_blocks_legacy\s*\(", data)) != 3:
|
||||
raise SystemExit("B07b must retain exactly two legacy data consumers")
|
||||
|
||||
old_data = committed_source("data.c")
|
||||
for function in ("erofs_read_data", "erofs_read_uio"):
|
||||
body = extract_function(data, function)
|
||||
if body != extract_function(old_data, function):
|
||||
raise SystemExit(f"B07b changed consumer early: {function}")
|
||||
if "erofs_map_blocks_legacy(" not in body or "erofs_map_blocks(" in body:
|
||||
raise SystemExit(f"B07b migrated consumer early: {function}")
|
||||
for filename in ("internal.h", "super.c", "zdata.c", "zmap.c"):
|
||||
current = (dut / "src" / filename).read_text(encoding="utf-8")
|
||||
if current != committed_source(filename):
|
||||
raise SystemExit(f"B07b changed non-producer source: {filename}")
|
||||
|
||||
cleanup = Counter(
|
||||
(record["actual"]["acquire_count"], record["actual"]["release_count"])
|
||||
for record in tuples["records"]
|
||||
)
|
||||
if any(acquire != release for acquire, release in cleanup):
|
||||
raise SystemExit(f"metadata cleanup became unbalanced: {cleanup!r}")
|
||||
if any(record["actual"]["errno"] < 0 for record in tuples["records"]):
|
||||
raise SystemExit("negative errno observed")
|
||||
|
||||
summary = {
|
||||
"status": "PASS",
|
||||
"final_id": "P15-006",
|
||||
"map_cases": 80,
|
||||
"producer_cases": len(producer_ids),
|
||||
"compressed_control_cases": 30,
|
||||
"device_cases": 13,
|
||||
"producer_tuple_diff_count": len(producer_diffs),
|
||||
"full_tuple_diff_count": len(full_diffs),
|
||||
"producer_tuple_bytes": len(producer_bytes),
|
||||
"producer_tuple_bytes_sha256": producer_sha256,
|
||||
"full_tuple_bytes_sha256": result["tuple_bytes_sha256"],
|
||||
"model_sha256": result["model_sha256"],
|
||||
"legacy_data_consumers": 2,
|
||||
"compressed_consumers_migrated": False,
|
||||
"cleanup_distribution": {
|
||||
f"{acquire}:{release}": count
|
||||
for (acquire, release), count in sorted(cleanup.items())
|
||||
},
|
||||
"qemu": "NOT_RUN",
|
||||
"full_feature_suite": "NOT_RUN",
|
||||
}
|
||||
with output.open("x", encoding="ascii") as stream:
|
||||
json.dump(summary, stream, ensure_ascii=True, indent=2, sort_keys=True)
|
||||
stream.write("\n")
|
||||
print(
|
||||
"B07b PASS producer=50 diff=0 full=80 device=13 "
|
||||
f"producer-sha256={producer_sha256}"
|
||||
)
|
||||
PY
|
||||
then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B07b producer boundary check failed'
|
||||
fi
|
||||
|
||||
sha256sum "$artifacts/baseline/result.json" \
|
||||
"$artifacts/baseline/tuples.json" "$artifacts/baseline/devices.json" \
|
||||
"$artifacts/candidate/result.json" "$artifacts/candidate/tuples.json" \
|
||||
"$artifacts/candidate/devices.json" "$artifacts/producer-check.json" \
|
||||
>"$artifacts/SHA256SUMS"
|
||||
printf 'B07b PASS object producers; all consumers remain staged\n'
|
||||
Reference in New Issue
Block a user