Repository navigation
Commit 46b72c7
authored
Async profiler: V1 (TaskAsync) instrumentation + tests. (#129043)
### 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<VoidTaskResult>
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.1 parent 65b2508 commit 46b72c7
21 files changed
Lines changed: 8010 additions & 2296 deletions
File tree
- src
- coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices
- libraries
- System.Private.CoreLib/src
- System
- Runtime/CompilerServices
- Threading/Tasks
- System.Runtime/tests/System.Threading.Tasks.Tests
- System.Runtime.CompilerServices
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1043 | 1043 | | |
1044 | 1044 | | |
1045 | 1045 | | |
| 1046 | + | |
| 1047 | + | |
1046 | 1048 | | |
1047 | 1049 | | |
1048 | 1050 | | |
| |||
1228 | 1230 | | |
1229 | 1231 | | |
1230 | 1232 | | |
1231 | | - | |
| 1233 | + | |
1232 | 1234 | | |
1233 | 1235 | | |
1234 | 1236 | | |
| |||
1565 | 1567 | | |
1566 | 1568 | | |
1567 | 1569 | | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
| 1573 | + | |
| 1574 | + | |
| 1575 | + | |
| 1576 | + | |
| 1577 | + | |
| 1578 | + | |
| 1579 | + | |
1568 | 1580 | | |
1569 | 1581 | | |
1570 | 1582 | | |
| |||
1626 | 1638 | | |
1627 | 1639 | | |
1628 | 1640 | | |
1629 | | - | |
| 1641 | + | |
1630 | 1642 | | |
1631 | 1643 | | |
1632 | 1644 | | |
| |||
1647 | 1659 | | |
1648 | 1660 | | |
1649 | 1661 | | |
1650 | | - | |
| 1662 | + | |
1651 | 1663 | | |
1652 | 1664 | | |
1653 | 1665 | | |
| |||
1668 | 1680 | | |
1669 | 1681 | | |
1670 | 1682 | | |
1671 | | - | |
| 1683 | + | |
1672 | 1684 | | |
1673 | 1685 | | |
1674 | 1686 | | |
| |||
1685 | 1697 | | |
1686 | 1698 | | |
1687 | 1699 | | |
1688 | | - | |
| 1700 | + | |
1689 | 1701 | | |
1690 | 1702 | | |
1691 | 1703 | | |
| |||
1700 | 1712 | | |
1701 | 1713 | | |
1702 | 1714 | | |
| 1715 | + | |
| 1716 | + | |
| 1717 | + | |
| 1718 | + | |
| 1719 | + | |
1703 | 1720 | | |
1704 | 1721 | | |
1705 | 1722 | | |
1706 | | - | |
| 1723 | + | |
1707 | 1724 | | |
1708 | | - | |
1709 | | - | |
1710 | | - | |
1711 | | - | |
1712 | | - | |
1713 | | - | |
1714 | | - | |
1715 | | - | |
1716 | | - | |
| 1725 | + | |
1717 | 1726 | | |
1718 | 1727 | | |
1719 | 1728 | | |
| |||
0 commit comments