Skip to content

Async profiler: V1 (TaskAsync) instrumentation + tests.#129043

Merged
lateralusX merged 46 commits into
dotnet:mainfrom
lateralusX:lateralusX/async-profiler-asyncv1-support-v2
Jun 24, 2026
Merged

Async profiler: V1 (TaskAsync) instrumentation + tests.#129043
lateralusX merged 46 commits into
dotnet:mainfrom
lateralusX:lateralusX/async-profiler-asyncv1-support-v2

Conversation

@lateralusX

@lateralusX lateralusX commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

Enables the async profiler for the V1 TaskAsync (state-machine-based) async path. Both V1 (TaskAsync) and V2 (RuntimeAsync) now emit a uniform, well-defined event stream that downstream tools can consume without knowing which async model produced a given chain. There are smaller variations to what events V1 can support, but all-important events are supported on both async models. The callstack events are also typed, since their data will end up slightly different (Native IP only on V2 and Method Start native IP and state on V1).

PR includes extensive test suite covering both paths as well as refactoring.

Motivation

V1 async (the C#-compiler-generated state-machine IAsyncStateMachineBox model) is the dominant async path in the wild. Until now the async profiler only instrumented the V2 (RuntimeAsync) path, so V1 chains were invisible to consumers. This PR extends the instrumentation to V1 while keeping the event stream identical in shape between the two models.

What's in this PR

Runtime V1 instrumentation

  • AsyncStateMachineDispatcher — class deriving from Task that wraps the actual state-machine box, with per-invocation TLS-pushed state held in AsyncStateMachineDispatcherInfo (ref struct).
  • Per-dispatcher fields (LastContinuation, ReachedLastContinuation, InnerBox) track the cascade so we can emit accurate Resume, Suspend, Complete, and append events as chains grow.
  • Cooperative append mechanism: when a parent registers after a child has already started walking, the runtime emits AppendAsyncCallstack to backfill the visible chain. Three race outcomes are handled (parent-registers-before-child-completes, parent-registers-during-suspend, and the unrecoverable late-parent case which is a design limit).
  • StateMachineDiagnosticData / GetDiagnosticData plumbed through AsyncTaskMethodBuilderT, AsyncMethodBuilderCore, and IAsyncStateMachineBox to expose the walkable chain to the profiler. NativeAOT returns false from GetDiagnosticData due to lack of native method IP and state field access.
  • InstrumentCheckPoint guards at all V1 builder await-completion sites (TaskAwaiter, ValueTaskAwaiter, ConfiguredValueTaskAwaitable, YieldAwaitable, PoolingAsyncValueTaskMethodBuilderT), linked out when no event source support.

Async profiler V1 event model

The runtime emits Create + Resume + Suspend + Complete per dispatcher MoveNext.

Create/suspend callstacks is not emitted on V1. The continuation chain is built and finalized after emitting a create/suspend event. Create/Suspend callstacks can be calculated when parsing the whole trace since the next resume for that context will carry the callstack.

On V2, continuation chains are build and finalized before scheduled for execution. On V1 this happens in parallel and chains can end up truncated in case completion race between the thread yielding and the thread executing the resumed continuation chain. A continuation chain might also continue to build after a thread started to execute a continuation chain. To handle this a new event was added to async profiler, AppendAsyncCallstack, it can fire several times between a resume async context and its completion. This gives a parser the ability to recreate the full resumed async callstack at resume point, even if it didn't exist at that point during runtime.

Tests

Test files split by async model to keep each focused:

  • AsyncProfilerTests.cs — shared partial-class infrastructure: parsers, event listeners, scenario runners.
  • AsyncProfilerV1Tests.cs — tests under the TaskAsync_* prefix covering V1 scenarios.
  • AsyncProfilerV2Tests.cs — tests under the RuntimeAsync_* prefix covering V2 scenarios.

Total of 116 tests covering both V1 and V2 scenarios.

Out of scope (follow-ups)

  • Using the AsyncInstrumentation opens up for folding existing TPL and debugger checks into the same guard. This will be handled in a follow-up PR and could offer performance improvements on existing TPL and debugger paths where async profiler co-exists.
  • Code currently uses a dispatcher box put at the head of continuation chain to push/pop needed async dispatcher info on tls. There are some code paths that are known (thread pool, default sync context and scheduler), we could optimize those paths if we knew they are always taken at create location, removing the allocation. Having that said, every continuation in the chain is allocated, so might not be a big deal in the end anyways.
  • AsyncV1 have a late attach issue, same applies to TPL. Unless we always create/enable the async dispatcher info for AsyncV1, there will be potential blind spots in the stack when doing late attach and profiler have not been enabled at startup.
  • PoolingAsyncValueTaskMethodBuilderT instrumentation, can be added later, if needed.
  • NativeAOT V1 callstack support. NativeAOT async support is already limited in tooling. Since lack of async callstacks will void the majority of scenarios, NAOT is currently not supported on V1. Could be added in future if needed
  • Run V1 tests on Mono. AsyncProfiler + V1 instrumentation builds on Mono, but currently no tests are executed. Can be revisited later.

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

Extends the async-profiler runtime instrumentation to cover the V1 Task/state-machine (compiler-generated) async path, aiming to make V1 and V2 produce a uniform async-profiler event stream (including callstacks and V1 append/backfill behavior), and adds a large test suite split by async model.

Changes:

  • Adds V1 instrumentation via an AsyncTaskDispatcher wrapper (plus TLS-pushed dispatcher state) and inserts instrumentation checkpoints across key await/builder scheduling sites.
  • Plumbs continuation-walk diagnostics through IAsyncStateMachineBox.GetDiagnosticData and adds AsyncStateMachineDiagnostics<TStateMachine> to support V1 callstack capture.
  • Adds/organizes async-profiler tests into V1/V2-specific files and updates the test project to compile them.

Reviewed changes

Copilot reviewed 17 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Threading.Tasks.Tests.csproj Includes new V1/V2 async-profiler test files in the test project.
src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV1Tests.cs Adds V1 (TaskAsync) async-profiler scenario coverage and validations.
src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/System.Runtime.CompilerServices/AsyncProfilerV2Tests.cs Adds V2 (runtime-async) async-profiler scenario coverage and validations.
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs Wraps scheduled async state-machine boxes with dispatcher when async-profiler instrumentation is enabled.
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs Exposes continuation object for diagnostics to support continuation walking.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/TaskAwaiter.cs Wraps state-machine box with dispatcher in key await continuation paths under async-profiler.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs Adds dispatcher wrapping in ValueTask await continuation paths under async-profiler.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs Adds dispatcher wrapping in configured ValueTask await continuation paths under async-profiler.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/YieldAwaitable.cs Adds dispatcher wrapping in Yield await continuation paths under async-profiler.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/IAsyncStateMachineBox.cs Adds GetDiagnosticData API to support profiler continuation walking.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncTaskMethodBuilderT.cs Emits V1 method/unwind instrumentation and implements diagnostic-data plumbing for state-machine boxes.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs Adds a stub GetDiagnosticData implementation returning false (not yet supported).
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncTaskDispatcher.cs Introduces dispatcher wrapper + TLS state used for V1 Create/Resume/Complete + append callstack behavior.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncStateMachineDiagnostics.cs Adds per-state-machine cached method-id + state-field-offset resolution for diagnostics.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderCore.cs Adds helper to recover an IAsyncStateMachineBox from continuation Actions/wrappers.
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncProfiler.cs Adds AppendAsyncCallstack event and shared callstack emission/walking logic used by V1.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncProfiler.CoreCLR.cs Refactors V2 callstack emission to use shared helpers and adjusts suspend emission ordering.
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems Wires new compiler-services files and adjusts shared inclusion for async-profiler/instrumentation sources.

Comment thread src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs Outdated
@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

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

Comments suppressed due to low confidence (1)

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:7232

  • Typo in nearby comment: "istelf" -> "itself".
        internal object? ContinuationForDiagnostics => m_continuationObject != this ? m_continuationObject : null;

        internal virtual Delegate[]? GetDelegateContinuationsForDebugger()
        {
            // Avoid an infinite loop by making sure the continuation object is not a reference to istelf.
            if (m_continuationObject != this)

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

Copilot reviewed 19 out of 21 changed files in this pull request and generated 6 comments.

@noahfalk

Copy link
Copy Markdown
Member

@tarekgh - do you know who reviews System.Threading.Task stuff with Toub not around? I'll be looking at this and wouldn't be surprised if @jkotas does too, but wanted to give a heads up if there is a BCL owner that would also like to review?

@tarekgh

tarekgh commented Jun 11, 2026

Copy link
Copy Markdown
Member

@noahfalk the owners are @dotnet/area-system-threading-tasks as it stated in the doc https://github.com/dotnet/runtime/blob/main/docs/area-owners.md.

Copilot AI review requested due to automatic review settings June 22, 2026 15:30
@lateralusX lateralusX force-pushed the lateralusX/async-profiler-asyncv1-support-v2 branch from c1b75ba to 0edf9d1 Compare June 22, 2026 15:30

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

Copilot reviewed 18 out of 21 changed files in this pull request and generated 4 comments.

@lateralusX

Copy link
Copy Markdown
Member Author

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@lateralusX

Copy link
Copy Markdown
Member Author

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@lateralusX

Copy link
Copy Markdown
Member Author

/ba-g Failure in runtime (Build Libraries Test Run release coreclr windows x86 Release) appears on other PR's. Failure/Cancel in runtime-nativeaot-outerloop is unrelated and appears on other PR's.

@lateralusX lateralusX merged commit 46b72c7 into dotnet:main Jun 24, 2026
168 of 174 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jun 25, 2026
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.

6 participants