ReviewOS

zig-utils/zig-js

Private ABI: canonicalize heap snapshot StringData names

#703
Closed chris opened this 9 days ago ·
9 days ago

Parents: #495, #473 Original heap-snapshot implementation: #403 Related StringData boundary fixes: #672, #699, #701, #702

The Chrome/V8 heap-snapshot serializer currently interns StringCell.bytes directly as each string node name. That slice is a physical 1-byte-per-code-unit image for flat Latin-1 StringData, not canonical WTF-8. A reachable non-ASCII flat string can therefore produce invalid JSON or a semantically incorrect V8 strings table.

Scope

  • Canonicalize string-node names through the semantic StringData boundary before V8 string-table interning.
  • Preserve zero-copy borrowing for canonical/ASCII cells and bound temporary conversion lifetime to one snapshot transaction.
  • Preserve graph identities, node/edge ordering, shallow/external sizes, retained-size accounting, class tables, and stable IDs.
  • Keep the GCDebugging and heap-profile formats unchanged where they intentionally expose only the generic String class.
  • Propagate conversion OOM fail-atomically with no partial output or leaked temporary storage.
  • Add a reachable non-ASCII flat-Latin-1 witness that parses the complete V8 JSON and verifies semantic string-table content.
  • Verify Debug, forced-flat-Latin-1, ReleaseSafe, TSan, private ABI audits/fixtures, and the full unit suite.
  • Record applicable Test262 impact; this private profiler ABI has no Test262 subtree.

Excluded: Promise/microtask/Worker job scheduling, stack/queue work, and shared Map/Set iterator cursor work (#643).

9 days ago

Implementation is in focused verification. The audit found both observable consumers of per-cell names: the V8 JSON strings table and the complete/root rows in the Markdown heap profile. Both now obtain canonical WTF-8 through the StringData boundary; GCDebugging remains allocation-free for names because it intentionally emits only the generic String class.

Exact stale-reader proof under the flat-storage switch: the pre-fix physical-bytes path failed the new reachable caféÿ witness at the semantic strings-table assertion (0 passed / 1 failed); the repaired path passed it (1 / 1) and also preserves the profile name. Standard Debug passed the witness and the existing allocation-failure/dominator cluster. Home/Bun heap-snapshot fixtures passed in Debug, ReleaseSafe, and TSan, with exact audits at Home 447/447 and Bun 437/437 implemented private symbols.

The remaining optimized unit/full-suite runs are pending a quiet local build window. A prior ReleaseSafe compile stopped with filesystem NoSpaceLeft; I removed only the reproducible .zig-cache objects (about 20 GB freed), but a separate Home Zig process is active now, so I am not competing with it.

9 days ago

Implemented and pushed to main in b959adf7, fix(heap-snapshot): canonicalize StringData node names (#703).

The V8 strings table and Markdown heap profile now canonicalize StringData cell names through Value.asWtf8; a snapshot-scoped arena retains converted flat Latin-1 names exactly through serialization and frees them transactionally. GCDebugging remains unchanged and does no name conversion because its schema exposes only the generic String class. The profile caches one canonical name per node, so root and complete-cell rows do not transcode twice.

Evidence:

  • exact forced-flat stale reader: 0 passed / 1 failed at semantic caféÿ lookup
  • repaired forced-flat, Debug, ReleaseSafe, TSan: 1 / 1 each, no leaks
  • allocation-failure and exact-dominator heap-snapshot cluster: 2 / 2
  • Home/Bun ownership fixtures: Debug, ReleaseSafe, TSan green
  • exact private ABI audits: Home 447/447; Bun 437/437 implemented private symbols
  • full units: 1,796 passed / 1 skipped / 0 failed / 0 leaked across six shards
  • Test262: 0 applicable cases and 0 flips (private profiler ABI)

No Promise, microtask, Worker/job, stack/queue, or shared Map/Set iterator code changed.

Sign in to comment on this issue.