You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With ReadyToRun enabled for CoreCLR browser-wasm (#132339), stopping an EventPipe session that requests method rundown traps the runtime:
DOTNET: Unhandled error: null function
RuntimeError: null function
at dotnet.native.wasm:wasm-function[1470]:0x7f04d
at dotnet.native.wasm:wasm-function[3180]:0x14108f
at dotnet.native.wasm:wasm-function[3182]:0x1418d4
at dotnet.native.wasm:wasm-function[1519]:0x880af
at dotnet.native.wasm:wasm-function[5522]:0x26c703
at dotnet.native.wasm:wasm-function[7782]:0x2d01f4
at dotnet.native.js:1:69544
at callUserCallback (dotnet.native.js:1:109091)
Symbolicated via dotnet.native.js.symbols (innermost first):
After the trap the runtime is dead — every subsequent call reports Assert failed: The runtime is not running.
Root cause
IterateModule calls ETW::MethodLog::SendEventsForNgenMethods (inlined in the stack above), whose entire body is gated on the module being R2R:
#ifdef FEATURE_READYTORUN
if (pModule->IsReadyToRun())
{
ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo());
while (mi.Next())
{
// Call GetMethodDesc_NoRestore instead of GetMethodDesc to avoid restoring methods at shutdown.
MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc_NoRestore();
if (hotDesc != NULL)
ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
}
return;
}
#endif
So this path is unreachable without R2R, which is why the failure appeared only once R2R was turned on.
SendMethodEvent is called with pNativeCodeStartAddress == NULL, so it uses pMethodDesc->GetNativeCode(), then:
TADDR start = MethodAndStartAddressToEECodeInfoPointer(pMethodDesc, pNativeCodeStartAddress);
if (start == 0) return; // only guards a null address
EECodeInfo codeInfo(start);
codeInfo.GetMethodRegionInfo(&methodRegionInfo); // unguarded
EECodeInfo::Init takes its Invalid: path when ExecutionManager::FindCodeRange (or JitCodeToMethodInfo) fails, leaving m_pJM = NULL. GetMethodRegionInfo then does GetJitManager()->JitTokenToMethodRegionInfo(...) — a virtual dispatch through a null pointer. On x64 that is a null-deref AV; on wasm a call_indirect through an empty function-table slot is reported as RuntimeError: null function.
EECodeInfo::IsValid() is the established guard for exactly this and is used in ~37 places across the VM, including neighbours making the identical call (perfmap.cpp, gccover.cpp). eventtrace.cpp had zero uses of it.
Two separable problems
Missing validity guard (latent on all platforms).SendMethodEvent and SendMethodILToNativeMapEvent use EECodeInfo without checking IsValid(). Addressed by adding the guard; the affected method is then skipped rather than killing the runtime.
R2R code addresses do not resolve on wasm (the actual gap). With the guard in place the crash disappears, but every R2R-precompiled method is silently omitted from the rundown, so traces are missing MethodLoad/MethodDCEnd events for all framework code. Two candidates, not yet distinguished:
R2R code ranges are not registered with ExecutionManager on wasm (ReadyToRunJitManager range registration missing/incomplete), or
GetNativeCode() for an unrestored R2R method on wasm returns something that is not a code address at all (plausibly a function-table index), which can never resolve through a range lookup.
Impact
Two conditions must both hold:
the session requests method rundown (NgenMethodDCEnd etc.), and
the module is R2R.
Observed in Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests:
BlazorEventPipeTestWithHeapDump — traps. In CI the promise returned by collectGcDump never settles, so the work item ran until the 90-minute Helix executor kill.
BlazorEventPipeTestWithMetrics — passes on the same R2R app; its session does not request method rundown, confirming condition (1) is required.
Reproduction
Local, with R2R on (the default for CoreCLR browser-wasm):
Bisected by rebuilding the same app with R2R off — same app, same dotnet.native.wasm:
build
System.Private.CoreLib served
collectGcDump
PublishReadyToRun=true
29,098,176 B (R2R image)
traps, runtime dead, promise never settles
PublishReadyToRun=false (clean rebuild)
5,622,045 B (IL/webcil)
resolves with a valid Nettrace payload
An incremental rebuild with -p:PublishReadyToRun=false does not re-stage the assemblies and still serves the R2R images, giving a false negative. Delete obj/ and bin/ first.
Ask
Item 2 above: make R2R method code addresses resolvable on wasm so rundown emits method events for precompiled code, and re-enable the tests skipped against this issue.
Note
This issue body was generated by GitHub Copilot from a local investigation (symbolication, source analysis and an R2R on/off bisect) and reviewed before posting.
Description
With ReadyToRun enabled for CoreCLR
browser-wasm(#132339), stopping an EventPipe session that requests method rundown traps the runtime:Symbolicated via
dotnet.native.js.symbols(innermost first):ETW::MethodLog::SendMethodEvent(MethodDesc*, ...)ETW::EnumerationLog::IterateModule(Module*, unsigned int)ETW::EnumerationLog::IterateAppDomain(unsigned int)stop_session(unsigned long long)server_loop_tick(void*)SystemJS_ExecuteDiagnosticServerCallbackAfter the trap the runtime is dead — every subsequent call reports
Assert failed: The runtime is not running.Root cause
IterateModulecallsETW::MethodLog::SendEventsForNgenMethods(inlined in the stack above), whose entire body is gated on the module being R2R:So this path is unreachable without R2R, which is why the failure appeared only once R2R was turned on.
SendMethodEventis called withpNativeCodeStartAddress == NULL, so it usespMethodDesc->GetNativeCode(), then:EECodeInfo::Inittakes itsInvalid:path whenExecutionManager::FindCodeRange(orJitCodeToMethodInfo) fails, leavingm_pJM = NULL.GetMethodRegionInfothen doesGetJitManager()->JitTokenToMethodRegionInfo(...)— a virtual dispatch through a null pointer. On x64 that is a null-deref AV; on wasm acall_indirectthrough an empty function-table slot is reported asRuntimeError: null function.EECodeInfo::IsValid()is the established guard for exactly this and is used in ~37 places across the VM, including neighbours making the identical call (perfmap.cpp,gccover.cpp).eventtrace.cpphad zero uses of it.Two separable problems
Missing validity guard (latent on all platforms).
SendMethodEventandSendMethodILToNativeMapEventuseEECodeInfowithout checkingIsValid(). Addressed by adding the guard; the affected method is then skipped rather than killing the runtime.R2R code addresses do not resolve on wasm (the actual gap). With the guard in place the crash disappears, but every R2R-precompiled method is silently omitted from the rundown, so traces are missing
MethodLoad/MethodDCEndevents for all framework code. Two candidates, not yet distinguished:ExecutionManageron wasm (ReadyToRunJitManagerrange registration missing/incomplete), orGetNativeCode()for an unrestored R2R method on wasm returns something that is not a code address at all (plausibly a function-table index), which can never resolve through a range lookup.Impact
Two conditions must both hold:
NgenMethodDCEndetc.), andObserved in
Wasm.Build.Tests.Blazor.EventPipeDiagnosticsTests:BlazorEventPipeTestWithHeapDump— traps. In CI the promise returned bycollectGcDumpnever settles, so the work item ran until the 90-minute Helix executor kill.BlazorEventPipeTestWithMetrics— passes on the same R2R app; its session does not request method rundown, confirming condition (1) is required.Reproduction
Local, with R2R on (the default for CoreCLR browser-wasm):
Or directly in the browser against a built app:
Bisected by rebuilding the same app with R2R off — same app, same
dotnet.native.wasm:System.Private.CoreLibservedcollectGcDumpPublishReadyToRun=truePublishReadyToRun=false(clean rebuild)NettracepayloadAsk
Item 2 above: make R2R method code addresses resolvable on wasm so rundown emits method events for precompiled code, and re-enable the tests skipped against this issue.
Note
This issue body was generated by GitHub Copilot from a local investigation (symbolication, source analysis and an R2R on/off bisect) and reviewed before posting.