zig-js

Debugging & tooling

On this page 7

Inspecting running JavaScript

zig-js exposes an embedder-transported inspector protocol (zig-js-inspector/0.1) through include/zig-js/Extensions.h. Public JavaScriptCore inspectability stays opt-in: JSGlobalContextSetInspectable(ctx, true) must run before ZJSInspectorSessionCreate succeeds.

A session receives JSON messages through a synchronous callback and accepts JSON requests through ZJSInspectorSessionDispatch; message bytes are borrowed only for the duration of the call. Worker targets are isolated. The full domain list, trust boundary, and current debugger boundary are in Inspector protocol.

Script identity and source locations come from Context.registerDebugScript / registerDebugScriptWithLocations.

Diagnosing engine behaviour

The corpus runner doubles as the fastest engine probe:

zig build test262-bin
zig-out/bin/test262 --eval /tmp/probe.js       # OK <value> / ERR <name: msg>
zig-out/bin/test262 --diag test/language       # outcome<TAB>path<TAB>reason, clustered

--diag's third column clusters failures by cause, which is how you tell "one root cause across forty tests" from "forty distinct bugs":

zig-out/bin/test262 --diag test/language 2>/dev/null \
  | grep -av '^#' | cut -f3 | sort | uniq -c | sort -rn | head -30

--diag has no per-test timeout, so a full test/language pass takes minutes. A DebugAllocator leak report on stderr is harmless for these modes; the full-suite summary, by contrast, goes to stderr and must not be discarded.

For threading, the corpus binary is the cheap loop:

zig build threads-test-bin                            # ~40 s
zig-out/bin/threads-test one <case.js>
zig-out/bin/threads-test parallel-js one <case.js>

Sanitizers

zig build test -Dtsan=true                     # ThreadSanitizer
zig build test -Dtsan=true -Dtest-filter=parallel_js
zig build threadfuzz -Dtsan=true -Dfuzz-iters=60
zig build threadfuzz -Doptimize=ReleaseSafe -Dfuzz-iters=400
zig build test-objc-api-sanitize               # ASan + UBSan (macOS bridge)
zig build test-objc-api-leaks                  # macOS leak checker

ReleaseSafe is a different bug class from TSan: it keeps Zig's safety checks (UB, bounds, overflow, unreachable) on under the optimizer and catches codegen-dependent faults the Debug build hides.

Reading a TSan result: "still fails with suppressions applied" is frequently the case's own functional assertion, not a sanitizer abort — read the actual FAIL line. The suppression boundary itself is a documented decision, not an oversight: see Memory model.

Profiles

zig build threads-profile                                  # no-GIL contention baseline
zig build threads-profile -Dthreads-profile-case='condition asyncWait'
zig build midgc-profile                                    # mid-script parallel-GC convergence
zig build gc-profile                                       # allocation + Context lifecycle
zig build bench                                            # VM vs tree-walker

Profiles locate cost. They are not correctness gates and not publication evidence — publishing a number follows Benchmarks.

When a job looks hung

A run at high CPU for hours may be spinning, not slow. Sample it before believing the wall clock — on macOS:

sample <pid> 3 -f /tmp/out.txt

That has turned a "the tests are slow" belief into a diagnosed infinite loop in seconds. Two related traps:

  • Never run two corpus jobs at once. Some drivers reap survivors by process name and will kill the other run's cases, which are then silently recorded as failures or timeouts. Check pgrep -f 'zig-out/bin/threads-test' first.
  • Do not run corpus cases beside the unit suite. The suite makes no visible progress for ten minutes and looks hung; that is starvation.

Build cache

All build output lives in two repository-local directories:

PathContents
.zig-cache/compiled objects, scratch, hashes
zig-out/installed artifacts

Everything in both is reproducible — deleting them only forces a rebuild, and neither ever contains source or user-owned files. Test-name filters are runtime selectors and reuse the linked binary; target, optimization, sanitizer, and source changes can still grow the cache. Inspection and safe reclamation: Build cache, or run tools/zig-cache-tool.ts through Home.

Build-cost expectations

ActionRough cost
zig build test relink after any src/*.zig edit~4–6 min
Changing only -Dtest-filter after the test artifact existsno relink
Full unit suite via zig build test-parallel3 min across 10 shards
zig build threads-test-bin~40 s
Cold test262 -Doptimize=ReleaseFast build~25–30 min

Prefer one instrumentation pass that answers several questions over several narrow probes.