[browser][coreCLR] EventPipe CPU sampling for RyuJIT/R2R on WASM - #132788
Draft
pavelsavara wants to merge 1 commit into
Draft
[browser][coreCLR] EventPipe CPU sampling for RyuJIT/R2R on WASM#132788pavelsavara wants to merge 1 commit into
pavelsavara wants to merge 1 commit into
Conversation
Adds cooperative, self-triggered EventPipe CPU sampling for RyuJIT-generated R2R (native wasm) code on single-threaded browser/WASI, complementing the interpreter sampling from dotnet#126324. On single-threaded WASM there is no sampling thread, so sampling is driven by the instrumented code itself. - New JIT helper CORINFO_HELP_WASM_PROF_SAMPLEPOINT and R2R helper READYTORUN_HELPER_WasmProfSamplepoint (0x119, R2R minor 27.1). Native shim JIT_WasmProfSamplepoint in vm/wasm/helpers.cpp (modeled on JIT_PollGC) anchors a cooperative managed stack walk via an InlinedCallFrame / INLINED_PINVOKE_FROM_R2R derived from the caller's shadow SP, then delegates to the shared SamplingProfiler_OnSamplepoint. No GC transition; only walks on an actual sample. - New GT_WASM_PROF_SAMPLEPOINT node and PHASE_WASM_PROF_INSTRUMENT: emit a samplepoint at method entry and every loop back-edge for methods matching the WasmPerformanceInstrumentation MethodSet filter (same key and shared skip counter as the interpreter). Codegen emits it via genEmitHelperCall with the managed (sp, pep) ABI, like the throw helpers. - crossgen2 maps the helper to its R2R cell; the WebAssembly SDK forwards the filter to crossgen (PublishReadyToRunCrossgen2ExtraArgs) and warns that framework and BCL frames are absent from samples (the runtime-pack R2R images are not instrumented). v1 uses an unconditional helper call (skip counter inside the helper). The inline countdown fast path and the browser DevTools flame-chart profiler (PROF_ENTER/LEAVE plus an EH-dispatcher unwind hook) are deferred; see the PR description TODO. Validated: browser CoreCLR clr+host build clean; crossgen2 with WasmPerformanceInstrumentation=* over all of System.Private.CoreLib completes cleanly and the instrumented R2R image is ~6% larger than baseline (samplepoints emitted); the SDK warning fires on R2R publish. End-to-end runtime .nettrace capture is blocked by pre-existing experimental CoreCLR-browser sample-publish infrastructure issues (see TODO), not by this change.
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds cooperative, self-triggered EventPipe CPU sampling for RyuJIT-generated R2R (native wasm) code on single-threaded browser/WASI, complementing the interpreter sampling delivered in #126324.
On single-threaded WASM there is no sampling thread that can suspend the target, so sampling has to be self-triggered by the instrumented code — the same approach the interpreter uses via
INTOP_PROF_SAMPLEPOINT. This PR brings the equivalent to R2R code.Status: draft / v1. Compiles and is validated at the codegen + SDK-plumbing level; the end-to-end runtime
.nettracecapture is blocked by pre-existing experimental-path infra (see TODO below), not by this change.How it works
CORINFO_HELP_WASM_PROF_SAMPLEPOINTand R2R helperREADYTORUN_HELPER_WasmProfSamplepoint(0x119, R2R minor bumped to27.1). The native shimJIT_WasmProfSamplepointinvm/wasm/helpers.cpp(modeled onJIT_PollGC) publishes the caller's shadow SP, thenJIT_WasmProfSamplepointImplanchors a cooperative managed stack walk with anInlinedCallFrame/INLINED_PINVOKE_FROM_R2Rderived from that SP and delegates to the sharedSamplingProfiler_OnSamplepoint. StaysMODE_COOPERATIVEthroughout — no GC transition — and only pushes the anchor frame / walks on an actual sample.GT_WASM_PROF_SAMPLEPOINT+PHASE_WASM_PROF_INSTRUMENT(runs just beforePHASE_WASM_VIRTUAL_IPso the per-block Virtual IP store lands ahead of each samplepoint). It emits a samplepoint at method entry and on every loop back-edge (a DFS back-edge walk; it deliberately does not skipBBF_GC_SAFE_POINTblocks, since with an uninstrumented framework a loop calling only BCL methods would otherwise never sample). Gated on theWasmPerformanceInstrumentationMethodSet filter — same config key and shared adaptive skip counter as the interpreter. Codegen emits it throughgenEmitHelperCallwith the managed(sp, pep)ABI, exactly like the throw helpers (the proven WASM-R2R managed-helper path; the plaingtNewHelperCallNodepath assertsIAT_VALUEand can't be used for R2R indirection cells).--codegenopt:WasmPerformanceInstrumentation=...viaPublishReadyToRunCrossgen2ExtraArgs) and emits a<Warning>that framework/BCL frames are absent from samples (the runtime-pack R2R images are not instrumented).What was validated
clr+hostbuild (VM + JITclrjit_universal_wasm+ crossgen2 + host) — clean.--codegenopt:WasmPerformanceInstrumentation=*over all ofSystem.Private.CoreLibcompletes cleanly (robust across every method incl. loops, funclets, EH).browser-eventpipesample builds for CoreCLR; the SDK warning fires on R2R publish.TODO (for whoever continues after the blockers are resolved)
Runtime
.nettracecapture is not yet done — blocked by pre-existing experimental CoreCLR-browser R2R sample-publish infrastructure, independent of this change:PublishTrimmed=true, the intended path): intra-build self-lock — the publish rebuildsILLink.Tasks.dllinside the same graph that has the trimmer task-host loaded (MSB3027/MSB3021). Not fixable viaMSBUILDDISABLENODEREUSE=1/DOTNET_CLI_USE_MSBUILD_SERVER=0//nodeReuse:falseor pre-buildingILLink.Tasks(all tried).PublishTrimmed=false): gets past that, thenManagedToNativeGeneratorfails to resolveSystem.Private.CoreLibviaMetadataLoadContext(src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs:100). This path got furthest and is likely the smaller lift.Once a sample can be published: the
browser-eventpipesample only exposescollectCpuSamplesinteractively (DevTools console), so add a programmaticcollectCpuSamplescall to itsmain.jsto capture a.nettraceheadless, then assert the trace contains R2R frames with resolvable method names and that mixed R2R↔interpreter stacks unwind throughWasmR2RToInterpreterThunkNode/WasmInterpreterToR2RThunkNode.Deferred by design (follow-up PRs), per the agreed plan:
GCPOLL_INLINE-shaped inline countdown needs a new JIT-EE API (getAddrOfWasmSamplepointCounter, cloned fromgetAddrOfCaptureThreadGlobal) + an R2RIndirectWasmSamplepointCountercell — deferred to keep v1 tractable.PROF_ENTER/PROF_LEAVE). Needs an explicitBrowserProfiler_OnFrameUnwoundhook in the R2R EH dispatcher (RyuJIT epilogs don't run on the throw path, soPROF_LEAVEwould be missed and the shadow stack would drift).TARGET_BROWSER-only.Notes for reviewers:
JITHELPER;getHelperFtnAddrauto-wraps it in aPortableEntryPointon WASM (FEATURE_PORTABLE_ENTRYPOINTS).CorInfoHelpFuncvalue → JIT-EE version GUID bumped.Note
This pull request (branch, commits, and description) was generated with the assistance of GitHub Copilot.