Skip to content

Async Profiler: Combine instrumentation flags usage over profiler/tpl/debugger.#130083

Open
lateralusX wants to merge 2 commits into
dotnet:mainfrom
lateralusX:lateralusX/async-profiler-async-instrumentation-check-folding
Open

Async Profiler: Combine instrumentation flags usage over profiler/tpl/debugger.#130083
lateralusX wants to merge 2 commits into
dotnet:mainfrom
lateralusX:lateralusX/async-profiler-async-instrumentation-check-folding

Conversation

@lateralusX

@lateralusX lateralusX commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Reworks AsyncInstrumentation so all three instrumentation clients — the async profiler, the async debugger, and the TPL event source — flow through a single unified flags gate, and so that the per-client support checks constant-fold when a feature area is compiled out. This lets the ILLinker dead-code-eliminate the instrumentation paths it can prove are unreachable.

Motivation

Previously the async instrumentation entry points were gated on a monolithic AsyncInstrumentation.IsSupported (Debugger.IsSupported || EventSource.IsSupported) plus ad-hoc checks (e.g. TPL used TplEventSource.Log.IsEnabled() directly, V2 dispatch used RuntimeAsyncInstrumentationHelpers.InstrumentCheckPoint). Because the support check mixed all clients together, the trimmer couldn't remove a given client's instrumentation even when its underlying feature switch was disabled. Since we already need to load and sync the flags, folding TPL+Debugger checks into the same flag check reduce the instructions executed on hot path. The DCE guards also make sure we can eliminate the complete instrumentation block or just individual instrumentation calls depending on what features have been enabled.

It has also been verified that the DCE guards correctly get const folded in Tier1. AsyncInstrumentation.IsActive ends up in one load of s_activeFlags, a compare against 0 and a highly predictable branch bypassing the whole instrumentation block when disabled. The different AsyncInstrumentation.IsEnabled.AsyncProfiler|AsyncDebugger|Tpl all ends up as a simple bit check on the already loaded flags and a highly predicatalbe branch bypassing the respective instrumentation code when disabled.

What changed

  • Per-client capability checks backed by feature switches: - IsAsyncProfilerSupported / IsTplSupported → EventSource.IsSupported
  • IsAsyncDebuggerSupported → Debugger.IsSupported
  • IsSupported becomes the OR of the three.
  • IsEnabled.AsyncProfiler / AsyncDebugger / Tpl now && the corresponding IsXxxSupported capability. Since EventSource.IsSupported / Debugger.IsSupported are trimmer-substituted constants, these checks fold to false when the feature is off, making the guarded instrumentation DCE-able. All IsEnabled.* helpers are AggressiveInlining.
  • TPL promoted to a first-class client: new Flags.Tpl + DefaultTplFlags; TplEventSource.OnEventCommand now pushes state via AsyncInstrumentation.UpdateTplFlags(...), and TPL's continuation-scheduling logic in TaskContinuation.RunOrScheduleAction goes through IsEnabled.Tpl(flags) instead of touching TplEventSource.Log directly.
  • Split defaults: DefaultFlags → DefaultProfilerFlags, DefaultDebuggerFlags, DefaultTplFlags.
  • Unified load path: SyncActiveFlags() → LoadFlags(), plus LoadFlags(out Flags) and IsActive. The various *InstrumentCheckPoint properties are replaced by the IsActive + LoadFlags(out flags) pattern at every call site (TaskAwaiter, ValueTaskAwaiter, ConfiguredValueTaskAwaitable, YieldAwaitable, AsyncTaskMethodBuilder, PoolingAsyncValueTaskMethodBuilder, V2 AsyncHelpers.DispatchContinuations/FinalizeRuntimeAsyncTask).
  • V1 dispatcher AsyncStateMachineDispatcherInfo.IsSupported now keys off IsAsyncProfilerSupported (still false on NativeAOT, unchanged behavior).

Trimming result

Verified with ILLink: with both EventSource and Debugger disabled, virtually all async profiler code is removed. The only residue is the async instrumentation flags themselves and System.Runtime.CompilerServices.AsyncProfiler.Info.

Notes

  • Debugger coordination is preserved. Unlike the profiler/TPL clients (which push state via UpdateAsyncProfilerFlags/UpdateTplFlags), the debugger sets Task.s_asyncDebuggingEnabled directly and flips the Flags.Synchronize bit to trigger a re-sync at the next checkpoint. SynchronizeFlags keeps a live read of that field, and comments were added on both Task.s_asyncDebuggingEnabled and SynchronizeFlags documenting the contract.
  • No functional/behavioral change to what gets instrumented when a client is enabled — this is a gating/structure refactor. No test changes required.

Copilot AI review requested due to automatic review settings July 1, 2026 15:13
@lateralusX lateralusX changed the title Instrumentation flags usage over profiler/tpl/debugger.As¨¨ Async Profiler: Instrumentation flags usage over profiler/tpl/debugger. Jul 1, 2026
@lateralusX lateralusX changed the title Async Profiler: Instrumentation flags usage over profiler/tpl/debugger. Async Profiler: Combine instrumentation flags usage over profiler/tpl/debugger. Jul 1, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-threading-tasks
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors async instrumentation gating across CoreLib to consistently use AsyncInstrumentation’s active flag snapshot (covering async profiler, TPL EventSource, and debugger) instead of directly checking TplEventSource.Log.IsEnabled() / Task.s_asyncDebuggingEnabled / older checkpoint helpers.

Changes:

  • Introduces a Tpl client flag and new flag-loading APIs in AsyncInstrumentation, plus plumbing from TplEventSource.OnEventCommand to keep TPL state synchronized.
  • Updates multiple awaiter / continuation hot paths to load instrumentation flags once and branch based on AsyncProfiler vs Tpl vs AsyncDebugger.
  • Updates async-profiler dispatcher creation/completion APIs to take an explicit flags snapshot and adds platform support guards.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs Pushes TPL enable/disable state into AsyncInstrumentation via UpdateTplFlags.
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs Uses AsyncInstrumentation flags to decide async-profiler dispatcher wrapping and TPL tracing scheduling path.
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs Documents debugger/Synchronize bit coordination contract for async debugging enablement.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs Switches await continuation scheduling decisions to consult loaded instrumentation flags.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs Switches async-profiler dispatcher wrapping decisions to consult loaded instrumentation flags.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs Routes TaskWait* tracing and async-profiler dispatcher wrapping through loaded instrumentation flags.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs Updates async-profiler resume/complete/unwind checkpoints to load flags and pass snapshots; adjusts diagnostic-data gating to IsSupported.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs Switches async-profiler dispatcher wrapping decisions to consult loaded instrumentation flags.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncTaskMethodBuilderT.cs Switches async-profiler resume/complete/unwind checkpoints and TPL tracing to consult loaded instrumentation flags; adjusts diagnostic-data gating to IsSupported.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncStateMachineDispatcher.cs Renames/reshapes support checkpoints, threads flags snapshots through dispatcher creation/resume/complete/unwind, and consolidates flag loading.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncInstrumentation.cs Adds Tpl client support, splits “supported” checks, introduces LoadFlags APIs, and adds UpdateTplFlags + synchronization logic.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs Updates runtime-async instrumentation checkpoints to load and pass the active flag snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants