runtime/pprof — validation proof

Go’s own runtime/pprof 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/pprof row in Validated Test Packages.

Validated 2026-10-01 · converter 8289d30c9

145 matched · 7 disclosed — Go 1.24.13, windows/amd64, converted package src/core/runtime/pprof.

Measured at Release (tiered JIT off), oracle go version go1.24.13 windows/amd64.

Both runtimes skip 2 of the matched tests identically.

SIX host-fatal entries, all of one shape, all minted by Q43 on 2026-09-04 and each earned by its own isolation measurement: the awaitBlockedGoroutine closure described next. A SEVENTH entry – TestGoroutineProfileLabelRace, a HANG rather than a crash – was minted with them and RETIRED on 2026-09-05 (its record is the last note). Withdrawing the seven is what made the package measurable at all: on the 2026-09-04 tree the converted host went from 2 verdicts against 183 Go rows to 147 verdicts, 120 of them matching, in 48 seconds.

SIX host-fatal exclusions, minted together (Q43, 2026-09-04), are the complete reverse-reachability closure of runtime/pprof’s awaitBlockedGoroutine helper – computed with go/packages under the same conservative any-use-of-a-same-package-function rule the converter’s own capability analysis uses – and EACH was independently measured to take the host down when run alone. Withdrawing fewer would not make the package censusable: the next member kills the run the same way at the next alphabetical position. Together they carry 22 of the Go side’s 183 verdict rows.

THE MECHANISM. awaitBlockedGoroutine (pprof_test.go:1023) polls runtime.Stack(buf, all=true) for the regexp (?m)^goroutine \d+ [\]:\n(?:.+\n\t.+\n)*runtime/pprof\. -- it waits for a FOREIGN goroutine's own INTERIOR STACK FRAMES to appear in the all-goroutine dump. The CLR offers no way to walk another thread's stack, which the converted runtime.Stack(all) already states rather than hides: a foreign goroutine renders as its header, a ForeignStackPlaceholder line and its `created by` line. So the regexp can never match on any input, the helper's `for { Gosched(); Stack() }` loop never terminates, and the time.AfterFunc(t.Deadline()-1s) timer Go arms for exactly this case fires and panics ON A GOROUTINE. golib reproduces Go's semantics faithfully -- an unrecovered panic in a goroutine kills the binary -- so the process dies and every test after it in its phase is lost. The capability is absent BY CONSTRUCTION, not unimplemented: no increment of runtime/pprof can supply it.

WHY AN EXCLUSION RATHER THAN A DISCLOSED VERDICT. The host already attributes a verdict before it dies – Goroutine.ObserveUnhandledPanic writes <test> fail -- panic on a goroutine started by <test> with the panic text – so a verdict is not what is missing. Survival is, and no per-test capture can supply it: the polling loop keeps spinning whatever is done with the timer’s panic, so containing it converts a bounded crash into an unbounded hang and a package-deadline kill that discards every verdict in the package. That is the measured reflect TestOffsetLock shape core/testing/TestHost.cs carries as the reason its own containment policy was REVERSED, and the reason golib’s Goroutine.ContainUnhandledExceptions refuses a PanicException outright.

MINT RULE, checked: no committed proof page under docs/validation/current names any of the six, so the withdrawal cannot shrink a row some platform runs successfully. SIGNATURE, deliberately empty on all six: a withdrawn test produces no verdict on either side to pin against, and the loader refuses a signature only for this class for exactly that reason. RETIREMENT: all six retire together the day runtime.Stack(all) can render a foreign goroutine’s interior frames; until then the entries simply stop applying if it ever can.

THE SEVENTH ENTRY WAS A HANG, THE CLASS’S FIRST, AND IT IS RETIRED (2026-09-05, by the measurement its trigger named). TestGoroutineProfileLabelRace did not crash: its /reset subtest spun forever because the converted goroutine profile withheld LABELS (SUB-Q27’s measured pointer-staleness defect) and the subtest’s exit condition is a label appearing in the profile text. Measured alone it produced ZERO verdicts and consumed the whole package deadline (exit 1 after 182 s under -timeout 3m); in the full run it left 36 rows without a C# verdict. It sat in host-fatal rather than in the converter’s unsupportedRuntimeCapabilities gate on the gate’s own bar – ‘add an entry ONLY for something provably unavailable, never for something merely unimplemented’ – and the label half was unimplemented, not unavailable. RATIFIED, and the class WIDENED with it (coordinator ruling 2026-09-05, recorded at hostFatalClass in testConversion.go): host-fatal admits a crash OR a deadline-consuming hang, because the charter is ‘every test after it in its phase is lost too’ and a test that never returns costs exactly that. RETIREMENT, measured: the labels re-entered at the Q44 union (golib hands a reference-bearing box’s registered order TOKEN out as its unsafe.Pointer number, so the labelMap pointer resolves to its box across a collection instead of naming a moved address; pprof_impl.cs fills labels[i] from the goroutine registry’s slot). Re-measured at that union with this entry deleted, gated ^(TestGoroutineCounts|TestGoroutineProfileLabelRace)$, Release, tiering off, oracle go1.23.12: TestGoroutineProfileLabelRace PASS in 58 ms (/reset 42 ms, /churn 6 ms) and TestGoroutineCounts PASS in 10.7 s with its label half now reached – 4/4 verdicts matching. The entry is deleted rather than annotated because host-fatal retires by deletion; this note keeps the history so the shape is not re-derived. The six above are unchanged by it: their capability (a foreign goroutine’s interior frames in runtime.Stack(all)) is a different door.

Verdicts

Test go test go2cs
TestAtomicLoadStore64 pass pass
TestBlockProfileBias pass pass
TestCPUProfile pass pass
TestCPUProfileInlining pass skip (disclosed)
TestCPUProfileLabel pass pass
TestCPUProfileMultithreadMagnitude skip skip
TestCPUProfileMultithreaded pass pass
TestCPUProfileRecursion pass skip (disclosed)
TestCPUProfileWithFork pass pass
TestContextLabels pass pass
TestConvertCPUProfile pass pass
TestConvertCPUProfileNoSamples pass pass
TestConvertMemProfile pass pass
TestConvertMemProfile/allocs pass pass
TestConvertMemProfile/heap pass pass
TestDo pass pass
TestEmptyCallStack pass pass
TestEmptyStack pass pass
TestFakeMapping pass pass
TestGenericsHashKeyInPprofBuilder pass fail (disclosed)
TestGenericsInlineLocations pass fail (disclosed)
TestGoroutineCounts pass pass
TestGoroutineProfileConcurrency pass pass
TestGoroutineProfileConcurrency/finalizer_not_present pass pass
TestGoroutineProfileConcurrency/finalizer_present pass pass
TestGoroutineProfileConcurrency/goroutine_launches pass pass
TestGoroutineProfileConcurrency/goroutine_launches#01 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#02 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#03 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#04 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#05 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#06 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#07 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#08 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#09 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#10 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#11 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#12 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#13 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#14 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#15 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#16 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#17 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#18 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#19 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#20 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#21 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#22 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#23 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#24 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#25 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#26 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#27 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#28 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#29 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#30 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#31 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#32 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#33 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#34 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#35 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#36 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#37 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#38 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#39 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#40 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#41 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#42 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#43 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#44 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#45 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#46 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#47 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#48 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#49 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#50 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#51 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#52 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#53 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#54 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#55 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#56 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#57 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#58 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#59 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#60 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#61 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#62 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#63 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#64 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#65 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#66 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#67 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#68 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#69 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#70 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#71 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#72 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#73 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#74 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#75 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#76 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#77 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#78 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#79 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#80 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#81 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#82 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#83 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#84 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#85 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#86 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#87 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#88 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#89 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#90 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#91 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#92 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#93 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#94 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#95 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#96 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#97 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#98 pass pass
TestGoroutineProfileConcurrency/goroutine_launches#99 pass pass
TestGoroutineProfileConcurrency/overlapping_profile_requests pass pass
TestGoroutineProfileCoro pass pass
TestGoroutineProfileIssue74090 pass pass
TestGoroutineProfileLabelRace pass pass
TestGoroutineProfileLabelRace/churn pass pass
TestGoroutineProfileLabelRace/reset pass pass
TestGoroutineSwitch pass pass
TestHeapRuntimeFrames pass pass
TestLabelMapStringer pass pass
TestLabelRace pass pass
TestLabelSystemstack pass skip (disclosed)
TestMapping skip skip
TestMathBigDivide pass pass
TestMemoryProfiler pass pass
TestMemoryProfiler/debug=1 pass pass
TestMemoryProfiler/proto pass pass
TestMorestack pass skip (disclosed)
TestMutexBlockFullAggregation pass pass
TestMutexBlockFullAggregation/block pass pass
TestMutexBlockFullAggregation/mutex pass pass
TestProcSelfMaps pass pass
TestProcSelfMaps/Normal pass pass
TestProcSelfMaps/WithDeletedFile pass pass
TestSetGoroutineLabels pass pass
TestTimeVDSO pass pass
TestTracebackAll pass pass
TestTryAdd pass skip (disclosed)

Disclosed divergences

A disclosed divergence is a specific Go assertion the managed CLR provably cannot satisfy — never a tolerance, and never a test skipped to make a row pass. Each one is pinned by exact 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
TestCPUProfileInlining compiler-property containsInlinedCall(inlinedCaller, 4«10) at pprof_test.go:284. Go’s own compiler-inlining self-check, the first statement of the test, and the OWNER ruled the family STRUCTURAL on 2026-09-26 (ledger 4a122cd994, class I of docs/phase4/DESIGN-managed-profiling.md). The check scans the named function’s PC range with runtime.FuncForPC for a PC that resolves to a DIFFERENT function, which exists only where the Go compiler inlined a callee. The converted program carries no inlining decisions: each function owns a synthetic-PC span that resolves to that function by construction (golib/GoSyntheticPC.cs), so the check can never succeed and the test skips at the line Go’s source writes.
TestCPUProfileRecursion runtime-capability Go PASS / C# SKIP, admitted by the OWNER RULING of 2026-09-28 03:17 (ledger 435557647c). The test demands CPU samples attributed to runtime/pprof.inlinedCallee, a frame that exists only in the Go compiler’s inline tree; the managed sampler resolves physical frames and no inlined callee is ever sampled (bank rehearsal, union cd2fc7dba1: 0 of 137 on windows, 0 of 72 on linux, recursionCallee and recursionCaller sampled). testenv.CPUProfilingBroken() stays true, so the test is bounded at Go’s own 10 s and takes Go’s issue-13841 skip path; Go’s per-OS false is unusable here (G’s reading, ledger 2026-09-28 03:14: 2,606 s for one test, lostProfileEvent-dominated, no clean bound). Re-read every sweep; retires when the managed profiler can attribute a sample to an inlined Go callee.
TestGenericsHashKeyInPprofBuilder runtime-capability SHAPE-NAME half. Go names each generic instantiation’s frame with its GC shape (genericAllocFunc[go.shape.uint32]); the CLR’s StackFrame.GetMethod() returns the generic method DEFINITION (measured), so every instantiation’s managed frame is genericAllocFunc and the test’s want lines – printed only while the names differ – cannot match. Retires only if frame resolution can recover an instantiation’s shape. The signature is this half’s own evidence line in the printed profile: the uint32 sample with Go’s values [1 128 0 0] under the shape-less name, one of the test’s newest samples near each record’s top. It is never Go’s want = text, which is each record’s LAST line and is cut once the cumulative heap profile (Go’s test order, D4) passes the 64K record cap (TestExecution.MaxRecordCharacters).
TestGenericsHashKeyInPprofBuilder codegen-liveness IN-USE half. Go reads the 64 B and 256 B allocations as freed after the test’s GC (in-use 0 0); the managed profile still reports them in use ([1 64 1 64], [1 256 1 256]) because the discarded temporaries stay rooted for the frame’s lifetime: the CLR reports a frame’s slots live throughout, where Go’s liveness maps drop them at their last use. Retires with that liveness gap.
TestGenericsInlineLocations runtime-capability SHAPE NAMES ONLY. Go names each inlined generic frame with its GC shape (nonRecursiveGenericAllocFunction[go.shape.struct {},go.shape.struct { runtime/pprof.buf [128]uint8 }] and its swap); the CLR’s StackFrame.GetMethod() returns the generic method DEFINITION (measured), so both managed frames read nonRecursiveGenericAllocFunction and the expected sample and location cannot match. The pin is the managed stack WITH both generic frames present: until the generic-caller NoInlining rule (claude/g-inline-locations) the JIT inlined them under TieredCompilation=0 and the stack read TestGenericsInlineLocations;storeAlloc, a fixable defect this entry must not absorb – if either frame vanishes again the pin fails. Retires only if frame resolution can recover an instantiation’s shape.
TestLabelSystemstack runtime-capability Go PASS / C# SKIP, admitted by the OWNER RULING of 2026-09-28 03:17 (ledger 435557647c). The test requires CPU samples taken on runtime.systemstack carrying the goroutine’s labels; the managed runtime has no system stack, so no systemstack frame is ever sampled (the labelHog samples themselves carry key=value: 2,934 windows / 2,202 linux in the bank rehearsal). CPUProfilingBroken() stays true and the test takes Go’s issue-13841 skip path (G’s reading, ledger 2026-09-28 03:14, is why Go’s per-OS false is unusable). Re-read every sweep; retires only if the runtime gains a systemstack frame the sampler can see.
TestMorestack runtime-capability Go PASS / C# SKIP, admitted by the OWNER RULING of 2026-09-28 03:17 (ledger 435557647c). The test requires samples in runtime.newstack beneath growstack; CLR thread stacks do not grow by copying, so there is no newstack frame to sample (growstack’s own recursion IS sampled). CPUProfilingBroken() stays true and the test takes Go’s issue-13841 skip path (G’s reading, ledger 2026-09-28 03:14, is why Go’s per-OS false is unusable). Re-read every sweep; permanent while CLR stacks are fixed-size.
TestTryAdd compiler-property findInlinedCall(inlinedCallerDump, 4«10) at pprof_test.go:2265. Go’s own compiler-inlining self-check, the first statement of the test, and the OWNER ruled the family STRUCTURAL on 2026-09-26 (ledger 4a122cd994, class I of docs/phase4/DESIGN-managed-profiling.md). The check scans the named function’s PC range with runtime.FuncForPC for a PC that resolves to a DIFFERENT function, which exists only where the Go compiler inlined a callee. The converted program carries no inlining decisions: each function owns a synthetic-PC span that resolves to that function by construction (golib/GoSyntheticPC.cs), so the check can never succeed and the test skips at the line Go’s source writes. Its ten t.Run subtests are Go-only rows by construction (the skip precedes the fan-out), and the class withdraws them by name under this signature-matched root; a subtest that ever reaches the C# side compares strictly.

TestCPUProfileInlining is a compiler-property skip: go test reports pass and the converted suite reports skip, at the test’s own upstream check for a decision of the Go compiler (inlining) that the converted program does not carry; the owner ruled the family structural. It is pinned to that upstream message, and any subtest Go ran beneath it is listed as withdrawn, never counted as matched.

TestTryAdd is a compiler-property skip: go test reports pass and the converted suite reports skip, at the test’s own upstream check for a decision of the Go compiler (inlining) that the converted program does not carry; the owner ruled the family structural. It is pinned to that upstream message, and any subtest Go ran beneath it is listed as withdrawn, never counted as matched.

TestCPUProfileRecursion is a runtime-capability skip: go test reports pass and the converted suite reports skip, at a check for a runtime capability the managed host does not have. It is pinned to that skip message, so the row moves to a hard mismatch if the converted side ever skips for a different reason, or stops skipping.

TestLabelSystemstack is a runtime-capability skip: go test reports pass and the converted suite reports skip, at a check for a runtime capability the managed host does not have. It is pinned to that skip message, so the row moves to a hard mismatch if the converted side ever skips for a different reason, or stops skipping.

TestMorestack is a runtime-capability skip: go test reports pass and the converted suite reports skip, at a check for a runtime capability the managed host does not have. It is pinned to that skip message, so the row moves to a hard mismatch if the converted side ever skips for a different reason, or stops skipping.

A disclosed test that fails at its root never reaches its own case fan-out, so the subtest verdict rows go test reports underneath it have no converted counterpart to compare. Those rows are withdrawn with their disclosed root — none is claimed by the matched count above:

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.