runtime/debug — validation proof
Go’s own runtime/debug test suite, converted to C# by go2cs, built against the converted standard
library, run under the Go-semantics test host, and compared verdict for verdict against a clean
go test -json baseline of the same sources. This page is generated by the converter from that
comparison — it is the evidence behind the runtime/debug row in
Validated Test Packages.
Validated 2026-08-25 · converter e2182a59e
4 matched · 5 disclosed — Go 1.23.12, windows/amd64, converted package
src/core/runtime/debug.
Both runtimes skip 1 of the matched tests identically.
Verdicts
| Test | go test |
go2cs |
|---|---|---|
TestFreeOSMemory |
pass | fail (disclosed) |
TestReadGCStats |
pass | pass |
TestSetCrashOutput |
pass | pass |
TestSetGCPercent |
skip | skip |
TestSetMaxThreadsOvf |
pass | pass |
TestStack |
pass | fail (disclosed) |
TestWriteHeapDumpFinalizers |
pass | fail (disclosed) |
TestWriteHeapDumpNonempty |
pass | fail (disclosed) |
TestWriteHeapDumpTypeName |
pass | fail (disclosed) |
Disclosed divergences
A disclosed divergence is a specific Go assertion the managed CLR provably cannot satisfy — not
a skipped test and not a tolerance. Each one is pinned by exact failure signature in the package’s
hand-owned go2cs_test_disclosures.json;
a disclosed test that fails any other way is still a hard mismatch.
| Test | Class | Pinned reason |
|---|---|---|
TestFreeOSMemory |
codegen-liveness |
the test allocates 32 MB inline in its own frame, clears the only named reference, and then asserts — from inside that same still-running frame — that FreeOSMemory has returned the memory to the OS. Go’s per-safepoint liveness maps drop the allocation at its last use; the CLR reports a frame’s slots live for the frame’s whole lifetime, so the 32 MB is still rooted while the test is looking. Measured by the routing probe as a three-way control: the identical allocation behind a RETURNED call releases 33,689,600 B to the byte, and the inline form does not, invariant under Release and under untiered JIT. The first assert (HeapReleased must increase at all) PASSES on the S2/S3 high-water surface — 3,031,040 B of unrelated memory really is released — which is why the pin is the second assert’s prefix and stops before its run-varying byte counts |
TestStack |
host-limit |
the fifth frame() assert requires the testing framework’s own frame to name GOROOT/src/testing/testing.go, which is a property of Go’s test BINARY: its testing package is compiled from that source. The converted deployment’s testing package is the hand-owned host (src/core/testing) — the ONE-testing-package ruling’s design, not an unimplemented conversion — so no conversion recorded a position for it and it honestly reports its own .cs position, exactly as golib and the BCL do. STRUCTURAL AND PERMANENT: unlike the relocatable-single-file entries, this does NOT retire when the host publishes self-contained and single-file, because the host would still be hand-written C# with no line-for-line relationship to Go’s testing.go. It retires only if the test host itself becomes a conversion of Go’s testing package, which the ONE-testing-package ruling deliberately forecloses |
TestWriteHeapDumpFinalizers |
runtime-capability |
Go’s heap-dump format is a serialization of the Go heap through Go’s own type descriptors; the heap a managed build would describe is not a Go heap and has no such descriptors, so any managed rendering would be fabrication rather than implementation and no truthful form exists at any cost. Pinned AS FAILING under the class’s binding anti-laundering clause: this test checks only that the dump is non-empty and never parses it, so a one-byte write would pass it while proving nothing — writing that byte is forbidden by this class’s own text |
TestWriteHeapDumpNonempty |
runtime-capability |
Go’s heap-dump format is a serialization of the Go heap through Go’s own type descriptors; the heap a managed build would describe is not a Go heap and has no such descriptors, so any managed rendering would be fabrication rather than implementation and no truthful form exists at any cost. Pinned AS FAILING under the class’s binding anti-laundering clause: this test checks only that the dump is non-empty (size >= 1) and never parses it, so a one-byte write would pass it while proving nothing — writing that byte is forbidden by this class’s own text |
TestWriteHeapDumpTypeName |
runtime-capability |
Go’s heap-dump format is a serialization of the Go heap through Go’s own type descriptors; the heap a managed build would describe is not a Go heap and has no such descriptors, so any managed rendering would be fabrication rather than implementation and no truthful form exists at any cost. Pinned AS FAILING under the class’s binding anti-laundering clause: this test checks only that the dump is non-empty and never parses it, so a one-byte write would pass it while proving nothing — writing that byte is forbidden by this class’s own text |
Excluded declarations
Declarations filtered from both sides of the comparison, and therefore not claimed above:
Benchmark, Fuzz and Example declarations the converted host does not execute, plus any
test requiring a capability the managed runtime does not provide — a testing member the host
has not implemented, or a platform behavior it provably cannot reproduce. Each is named with
the capability it needs.
- ExampleSetCrashOutput_monitor (example): example execution is deferred to Phase 4D
- FuzzParseBuildInfoRoundTrip (fuzz): fuzz execution is deferred to Phase 4D