conformance: compare h5py's big-endian VL values corrected, confirm libhdf5 over-reads per run

The last 3 our-errors and 2 mismatches were documented as not ours but
still counted against us, on a heuristic (any big-endian VL mismatch) and
a fixed list.

- ref.py checks that the installed h5py returns big-endian VL elements
  with the file's bytes under a little-endian dtype (writing and reading
  a vlen('>f4') in memory) and, if so, relabels them with the file's byte
  order before hashing, marking the object `ref_fix`. The values are now
  compared: attr_datatypes.hdf5 /@vlen_uint64 and tcomplex_be.h5
  /VariableLengthDatasetFloatComplex are identical to ours (h5dump 1.14.6
  prints the same (1, 2), (3, 4, 5), (42)).
- ref_bugs.py re-reads each object h5py reads only through a libhdf5 bug
  in six processes with different heaps (import order, MALLOC_PERTURB_).
  Values the file determines are the same every time; these three change
  (6, 6 and 3 distinct results), so they are over-read memory, not data
  clawhdf5 could match. compare.py classifies a file `ref-bug` only when
  every difference is such an object confirmed in the same run.
- report.py: the ref-bug class, the evidence table, the corrected
  objects; test_ref.py covers both (run in the nightly job).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-27 22:26:32 -05:00
co-authored by Claude Opus 5.5
parent 425585ee71
commit 5c44630ea2
9 changed files with 379 additions and 65 deletions
+28 -2
View File
@@ -5,6 +5,9 @@ Writes <results_dir>/results.csv, results.json and summary.md.
File classes (first match wins):
hang, oom, crash, panic ours: timeout / allocation failure / signal / any panic (caught or not)
h5py-cannot-read libhdf5/h5py failed to open the file (or crashed/hung)
ref-bug every issue is an object we refuse that h5py reads only through a
libhdf5 bug, confirmed in this run by ref_bugs.py (its values
change with the reading process's heap)
our-error we fail to open, list, or read something h5py reads
mismatch we read something with different shape/values, or a different object set
ok
@@ -19,6 +22,20 @@ import sys
R = sys.argv[1]
RUNS = os.path.join(R, "runs")
# Objects ref_bugs.py confirmed in this run: h5py's values for them come from
# libhdf5 reading memory the file does not determine.
try:
REF_BUGS = {(b["file"], b["object"])
for b in json.load(open(os.path.join(R, "ref_bugs.json")))["read_bugs"] if b.get("confirmed")}
except (OSError, ValueError, KeyError):
REF_BUGS = set()
def is_ref_bug(rel, issue):
"""An our-error on reading an object that ref_bugs.py confirmed."""
kind, detail = issue[0], issue[1]
return kind == "our-error" and any(f == rel and detail.startswith(obj + ": error: ") for f, obj in REF_BUGS)
def load(d, name):
rc_p = os.path.join(d, name + ".rc")
@@ -86,6 +103,8 @@ mismatch_causes = collections.defaultdict(lambda: {"files": set(), "count": 0, "
panics = []
ref_only_errors = collections.Counter()
incomparable = collections.Counter()
# Values ref.py corrected for a known h5py bug: (file, object, fixes, same as ours)
ref_fixes = []
def add(bucket, key, file, example):
@@ -169,6 +188,8 @@ for rel in files:
elif a.get("hash") != b.get("hash"):
issues.append(("mismatch", f"{p}: values differ (h5py {a.get('dtype')} vs ours {b.get('dtype')})", "values", b | {"ref_head": a.get("head"), "ref_dtype": a.get("dtype")}))
ok = False
if a.get("ref_fix") and "hash" in b:
ref_fixes.append((rel, p, a["ref_fix"], a.get("hash") == b.get("hash")))
ra, oa = a.get("attrs") or {}, b.get("attrs") or {}
if "attrs_error" not in b and "attrs_error" not in a and not ref_unopened:
for an in sorted(set(ra) | set(oa)):
@@ -187,6 +208,8 @@ for rel in files:
issues.append(("mismatch", f"{p}@{an}: attr shape {x.get('shape')} vs ours {y.get('shape')}", "attr-shape", y | {"ref_dtype": x.get("dtype")}))
elif x.get("hash") != y.get("hash"):
issues.append(("mismatch", f"{p}@{an}: attr values differ (h5py {x.get('dtype')} vs ours {y.get('dtype')})", "attr-values", y | {"ref_head": x.get("head"), "ref_dtype": x.get("dtype")}))
if x.get("ref_fix") and "hash" in y:
ref_fixes.append((rel, f"{p}@{an}", x["ref_fix"], x.get("hash") == y.get("hash")))
if ok:
n_ok += 1
@@ -197,6 +220,8 @@ for rel in files:
cls = "panic"
elif ref_open_fail:
cls = "h5py-cannot-read"
elif issues and all(is_ref_bug(rel, i) for i in issues):
cls = "ref-bug"
elif ours_open_err:
cls = "our-error"
issues.append(("our-error", f"open: {ours_open_err}", ours_open_err, {}))
@@ -259,10 +284,11 @@ def ser(b):
json.dump({"rows": rows, "issues": issues_by_file, "root_causes": ser(root_causes), "mismatch_causes": ser(mismatch_causes),
"panics": panics, "incomparable": incomparable.most_common(), "ref_only_errors": ref_only_errors.most_common()},
"panics": panics, "incomparable": incomparable.most_common(), "ref_only_errors": ref_only_errors.most_common(),
"ref_fixes": ref_fixes, "ref_bugs_confirmed": sorted(REF_BUGS)},
open(os.path.join(R, "results.json"), "w"), indent=1)
classes = ["ok", "our-error", "mismatch", "h5py-cannot-read", "hang", "panic", "crash", "oom"]
classes = ["ok", "our-error", "mismatch", "h5py-cannot-read", "ref-bug", "hang", "panic", "crash", "oom"]
by_corpus = collections.defaultdict(collections.Counter)
for r in rows:
by_corpus[r["corpus"]][r["class"]] += 1