Skip to content

Commit 4302b80

Browse files
authored
Add managed entry points for raising Contention events (#87087)
* Add managed entry points for raising Contention events - Added the events similarly to how the PortableThreadPool events are currently set up. They may need further tweaking to make them work from NativeAOT. - The events are not raised anywhere from the managed side yet, it's expected that they will eventually be raised by a new Lock type and at least by NativeAOT's Lock
1 parent 5a69a5c commit 4302b80

22 files changed

+515
-88
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Debugger.cs" />
139139
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\EditAndContinueHelper.cs" />
140140
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\EventPipe.CoreCLR.cs" />
141-
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\NativeRuntimeEventSource.PortableThreadPool.NativeSinks.CoreCLR.cs" Condition="'$(FeaturePortableThreadPool)' == 'true'" />
141+
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\NativeRuntimeEventSource.Threading.NativeSinks.CoreCLR.cs" />
142142
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\ICustomDebuggerNotification.cs" />
143143
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\StackFrame.CoreCLR.cs" />
144144
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\StackFrameHelper.cs" />
Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Threading;
5-
using System.Diagnostics.Tracing;
64
using System.Runtime.CompilerServices;
75
using System.Runtime.InteropServices;
86

@@ -14,31 +12,51 @@ internal sealed partial class NativeRuntimeEventSource : EventSource
1412
{
1513
[NonEvent]
1614
[LibraryImport(RuntimeHelpers.QCall)]
17-
internal static partial void LogThreadPoolWorkerThreadStart(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
15+
private static partial void LogContentionLockCreated(nint LockID, nint AssociatedObjectID, ushort ClrInstanceID);
1816

1917
[NonEvent]
2018
[LibraryImport(RuntimeHelpers.QCall)]
21-
internal static partial void LogThreadPoolWorkerThreadStop(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
19+
private static partial void LogContentionStart(
20+
ContentionFlagsMap ContentionFlags,
21+
ushort ClrInstanceID,
22+
nint LockID,
23+
nint AssociatedObjectID,
24+
ulong LockOwnerThreadID);
2225

2326
[NonEvent]
2427
[LibraryImport(RuntimeHelpers.QCall)]
25-
internal static partial void LogThreadPoolWorkerThreadWait(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
28+
private static partial void LogContentionStop(
29+
ContentionFlagsMap ContentionFlags,
30+
ushort ClrInstanceID,
31+
double DurationNs);
2632

2733
[NonEvent]
2834
[LibraryImport(RuntimeHelpers.QCall)]
29-
internal static partial void LogThreadPoolMinMaxThreads(ushort MinWorkerThreads, ushort MaxWorkerThreads, ushort MinIOCompletionThreads, ushort MaxIOCompletionThreads, ushort ClrInstanceID);
35+
private static partial void LogThreadPoolWorkerThreadStart(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
3036

3137
[NonEvent]
3238
[LibraryImport(RuntimeHelpers.QCall)]
33-
internal static partial void LogThreadPoolWorkerThreadAdjustmentSample(double Throughput, ushort ClrInstanceID);
39+
private static partial void LogThreadPoolWorkerThreadStop(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
3440

3541
[NonEvent]
3642
[LibraryImport(RuntimeHelpers.QCall)]
37-
internal static partial void LogThreadPoolWorkerThreadAdjustmentAdjustment(double AverageThroughput, uint NewWorkerThreadCount, ThreadAdjustmentReasonMap Reason, ushort ClrInstanceID);
43+
private static partial void LogThreadPoolWorkerThreadWait(uint ActiveWorkerThreadCount, uint RetiredWorkerThreadCount, ushort ClrInstanceID);
3844

3945
[NonEvent]
4046
[LibraryImport(RuntimeHelpers.QCall)]
41-
internal static partial void LogThreadPoolWorkerThreadAdjustmentStats(
47+
private static partial void LogThreadPoolMinMaxThreads(ushort MinWorkerThreads, ushort MaxWorkerThreads, ushort MinIOCompletionThreads, ushort MaxIOCompletionThreads, ushort ClrInstanceID);
48+
49+
[NonEvent]
50+
[LibraryImport(RuntimeHelpers.QCall)]
51+
private static partial void LogThreadPoolWorkerThreadAdjustmentSample(double Throughput, ushort ClrInstanceID);
52+
53+
[NonEvent]
54+
[LibraryImport(RuntimeHelpers.QCall)]
55+
private static partial void LogThreadPoolWorkerThreadAdjustmentAdjustment(double AverageThroughput, uint NewWorkerThreadCount, ThreadAdjustmentReasonMap Reason, ushort ClrInstanceID);
56+
57+
[NonEvent]
58+
[LibraryImport(RuntimeHelpers.QCall)]
59+
private static partial void LogThreadPoolWorkerThreadAdjustmentStats(
4260
double Duration,
4361
double Throughput,
4462
double ThreadPoolWorkerThreadWait,
@@ -53,29 +71,29 @@ internal static partial void LogThreadPoolWorkerThreadAdjustmentStats(
5371

5472
[NonEvent]
5573
[LibraryImport(RuntimeHelpers.QCall)]
56-
internal static partial void LogThreadPoolIOEnqueue(
74+
private static partial void LogThreadPoolIOEnqueue(
5775
IntPtr NativeOverlapped,
5876
IntPtr Overlapped,
5977
[MarshalAs(UnmanagedType.Bool)] bool MultiDequeues,
6078
ushort ClrInstanceID);
6179

6280
[NonEvent]
6381
[LibraryImport(RuntimeHelpers.QCall)]
64-
internal static partial void LogThreadPoolIODequeue(
82+
private static partial void LogThreadPoolIODequeue(
6583
IntPtr NativeOverlapped,
6684
IntPtr Overlapped,
6785
ushort ClrInstanceID);
6886

6987
[NonEvent]
7088
[LibraryImport(RuntimeHelpers.QCall)]
71-
internal static partial void LogThreadPoolWorkingThreadCount(
89+
private static partial void LogThreadPoolWorkingThreadCount(
7290
uint Count,
7391
ushort ClrInstanceID
7492
);
7593

7694
[NonEvent]
7795
[LibraryImport(RuntimeHelpers.QCall)]
78-
internal static partial void LogThreadPoolIOPack(
96+
private static partial void LogThreadPoolIOPack(
7997
IntPtr NativeOverlapped,
8098
IntPtr Overlapped,
8199
ushort ClrInstanceID);

src/coreclr/scripts/genRuntimeEventSources.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,12 @@ def getManifestsToGenerate(runtimeFlavor):
8585

8686
def generateEvent(eventNode, providerNode, outputFile, stringTable):
8787

88-
# ThreadPool events are defined manually in NativeRuntimeEventSource.PortableThreadPool.cs
88+
# ThreadPool and Contention events are defined manually in NativeRuntimeEventSource.Threading.cs
8989
symbol = eventNode.getAttribute("symbol")
9090
if "ThreadPool" in symbol:
9191
return
92+
if "Contention" in symbol:
93+
return
9294

9395
evtLevel = eventNode.getAttribute("level")[4:]
9496
evtKeywords = ""

src/coreclr/vm/nativeeventsource.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,40 @@ extern "C" void QCALLTYPE LogThreadPoolIOPack(_In_z_ void* nativeOverlapped, _In
153153

154154
END_QCALL;
155155
}
156+
157+
extern "C" void QCALLTYPE LogContentionLockCreated(void* LockID, void* AssociatedObjectID, uint16_t ClrInstanceID)
158+
{
159+
QCALL_CONTRACT;
160+
BEGIN_QCALL;
161+
162+
FireEtwContentionLockCreated(LockID, AssociatedObjectID, ClrInstanceID);
163+
164+
END_QCALL;
165+
}
166+
167+
extern "C" void QCALLTYPE LogContentionStart(
168+
uint8_t ContentionFlags,
169+
uint16_t ClrInstanceID,
170+
void* LockID,
171+
void* AssociatedObjectID,
172+
uint64_t LockOwnerThreadID)
173+
{
174+
QCALL_CONTRACT;
175+
BEGIN_QCALL;
176+
177+
FireEtwContentionStart_V2(ContentionFlags, ClrInstanceID, LockID, AssociatedObjectID, LockOwnerThreadID);
178+
179+
END_QCALL;
180+
}
181+
182+
extern "C" void QCALLTYPE LogContentionStop(uint8_t ContentionFlags, uint16_t ClrInstanceID, double DurationNs)
183+
{
184+
QCALL_CONTRACT;
185+
BEGIN_QCALL;
186+
187+
FireEtwContentionStop_V1(ContentionFlags, ClrInstanceID, DurationNs);
188+
189+
END_QCALL;
190+
}
191+
156192
#endif // FEATURE_PERFTRACING

src/coreclr/vm/nativeeventsource.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ extern "C" void QCALLTYPE LogThreadPoolIOEnqueue(_In_z_ void* nativeOverlapped,
2828
extern "C" void QCALLTYPE LogThreadPoolIODequeue(_In_z_ void* nativeOverlapped, _In_z_ void* overlapped, _In_z_ short ClrInstanceID);
2929
extern "C" void QCALLTYPE LogThreadPoolWorkingThreadCount(_In_z_ uint count, _In_z_ short ClrInstanceID);
3030
extern "C" void QCALLTYPE LogThreadPoolIOPack(_In_z_ void* nativeOverlapped, _In_z_ void* overlapped, _In_z_ short ClrInstanceID);
31+
extern "C" void QCALLTYPE LogContentionLockCreated(void* LockID, void* AssociatedObjectID, uint16_t ClrInstanceID);
32+
extern "C" void QCALLTYPE LogContentionStart(uint8_t ContentionFlags, uint16_t ClrInstanceID, void* LockID, void* AssociatedObjectID, uint64_t LockOwnerThreadID);
33+
extern "C" void QCALLTYPE LogContentionStop(uint8_t ContentionFlags, uint16_t ClrInstanceID, double DurationNs);
3134
#endif // defined(FEATURE_PERFTRACING)
3235

3336
#endif //_NATIVEEVENTSOURCE_H_

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ static const Entry s_QCall[] =
275275
DllImportEntry(LogThreadPoolIODequeue)
276276
DllImportEntry(LogThreadPoolIOPack)
277277
DllImportEntry(LogThreadPoolWorkingThreadCount)
278+
DllImportEntry(LogContentionLockCreated)
279+
DllImportEntry(LogContentionStart)
280+
DllImportEntry(LogContentionStop)
278281
DllImportEntry(EventPipeInternal_Enable)
279282
DllImportEntry(EventPipeInternal_Disable)
280283
DllImportEntry(EventPipeInternal_GetSessionInfo)

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,8 @@
14441444
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\IncrementingEventCounter.cs" />
14451445
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\IncrementingPollingCounter.cs" />
14461446
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\NativeRuntimeEventSource.cs" />
1447+
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\NativeRuntimeEventSource.Threading.cs" Condition="'$(FeaturePerfTracing)' != 'true' or '$(FeatureNativeAot)' == 'true'" />
1448+
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\NativeRuntimeEventSource.Threading.NativeSinks.cs" Condition="'$(FeaturePerfTracing)' == 'true' and '$(FeatureNativeAot)' != 'true'" />
14471449
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\PollingCounter.cs" />
14481450
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\RuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
14491451
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\Tracing\Winmeta.cs" />
@@ -2549,8 +2551,6 @@
25492551
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\CompleteWaitThreadPoolWorkItem.cs" />
25502552
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPool.Portable.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
25512553
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPool.Unix.cs" Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'" />
2552-
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\NativeRuntimeEventSource.PortableThreadPool.cs" Condition="'$(FeatureCoreCLR)' != 'true' and '$(FeatureMono)' != 'true'" />
2553-
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs" Condition="'$(FeatureCoreCLR)' == 'true' or '$(FeatureMono)' == 'true'" />
25542554
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PortableThreadPool.cs" />
25552555
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PortableThreadPool.Blocking.cs" />
25562556
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PortableThreadPool.GateThread.cs" />
Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Threading;
56
using System.Runtime.CompilerServices;
6-
using System.Diagnostics.CodeAnalysis;
77

88
namespace System.Diagnostics.Tracing
99
{
1010
// This is part of the NativeRuntimeEventsource, which is the managed version of the Microsoft-Windows-DotNETRuntime provider.
11-
// It contains the handwritten implementation of the ThreadPool events.
11+
// It contains the handwritten implementation of threading events.
1212
// The events here do not call into the typical WriteEvent* APIs unlike most EventSources because that results in the
1313
// events to be forwarded to EventListeners twice, once directly from the managed WriteEvent API, and another time
1414
// from the mechanism in NativeRuntimeEventSource.ProcessEvents that forwards native runtime events to EventListeners.
1515
// To prevent this, these events call directly into QCalls provided by the runtime (refer to NativeRuntimeEventSource.cs) which call
1616
// FireEtw* methods auto-generated from ClrEtwAll.man. This ensures that corresponding event sinks are being used
1717
// for the native platform.
18-
// For implementation of these events not supporting native sinks, refer to NativeRuntimeEventSource.PortableThreadPool.cs.
18+
// For implementation of these events not supporting native sinks, refer to NativeRuntimeEventSource.Threading.cs.
1919
[SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "NativeRuntimeEventSource is a special case where event methods don't use WriteEvent/WriteEventCore but still need to be instance methods.")]
2020
internal sealed partial class NativeRuntimeEventSource : EventSource
2121
{
22-
// This value does not seem to be used, leaving it as zero for now. It may be useful for a scenario that may involve
23-
// multiple instances of the runtime within the same process, but then it seems unlikely that both instances' thread
24-
// pools would be in moderate use.
25-
private const ushort DefaultClrInstanceId = 0;
26-
27-
private static class Messages
22+
private static partial class Messages
2823
{
24+
public const string ContentionLockCreated = "LockID={0};\nAssociatedObjectID={1};\nClrInstanceID={2}";
25+
public const string ContentionStart = "ContentionFlags={0};\nClrInstanceID={1};\nLockID={2};\nAssociatedObjectID={3}\nLockOwnerThreadID={4}";
26+
public const string ContentionStop = "ContentionFlags={0};\nClrInstanceID={1};\nDurationNs={2}";
2927
public const string WorkerThread = "ActiveWorkerThreadCount={0};\nRetiredWorkerThreadCount={1};\nClrInstanceID={2}";
3028
public const string MinMaxThreads = "MinWorkerThreads={0};\nMaxWorkerThreads={1};\nMinIOCompletionThreads={2};\nMaxIOCompletionThreads={3};\nClrInstanceID={4}";
3129
public const string WorkerThreadAdjustmentSample = "Throughput={0};\nClrInstanceID={1}";
@@ -37,17 +35,19 @@ private static class Messages
3735
}
3836

3937
// The task definitions for the ETW manifest
40-
public static class Tasks // this name and visibility is important for EventSource
38+
public static partial class Tasks // this name and visibility is important for EventSource
4139
{
40+
public const EventTask Contention = (EventTask)8;
4241
public const EventTask ThreadPoolWorkerThread = (EventTask)16;
4342
public const EventTask ThreadPoolWorkerThreadAdjustment = (EventTask)18;
4443
public const EventTask ThreadPool = (EventTask)23;
4544
public const EventTask ThreadPoolWorkingThreadCount = (EventTask)22;
4645
public const EventTask ThreadPoolMinMaxThreads = (EventTask)38;
4746
}
4847

49-
public static class Opcodes // this name and visibility is important for EventSource
48+
public static partial class Opcodes // this name and visibility is important for EventSource
5049
{
50+
public const EventOpcode LockCreated = (EventOpcode)11;
5151
public const EventOpcode IOEnqueue = (EventOpcode)13;
5252
public const EventOpcode IODequeue = (EventOpcode)14;
5353
public const EventOpcode IOPack = (EventOpcode)15;
@@ -57,6 +57,12 @@ private static class Messages
5757
public const EventOpcode Stats = (EventOpcode)102;
5858
}
5959

60+
public enum ContentionFlagsMap : byte
61+
{
62+
Managed,
63+
Native,
64+
}
65+
6066
public enum ThreadAdjustmentReasonMap : uint
6167
{
6268
Warmup,
@@ -66,9 +72,41 @@ public enum ThreadAdjustmentReasonMap : uint
6672
ChangePoint,
6773
Stabilizing,
6874
Starvation,
69-
ThreadTimedOut
75+
ThreadTimedOut,
76+
CooperativeBlocking,
77+
}
78+
79+
[Event(90, Level = EventLevel.Informational, Message = Messages.ContentionLockCreated, Task = Tasks.Contention, Opcode = EventOpcode.Info, Version = 0, Keywords = Keywords.ContentionKeyword)]
80+
private void ContentionLockCreated(nint LockID, nint AssociatedObjectID, ushort ClrInstanceID = DefaultClrInstanceId)
81+
{
82+
Debug.Assert(IsEnabled(EventLevel.Informational, Keywords.ContentionKeyword));
83+
LogContentionLockCreated(LockID, AssociatedObjectID, ClrInstanceID);
7084
}
7185

86+
[Event(81, Level = EventLevel.Informational, Message = Messages.ContentionStart, Task = Tasks.Contention, Opcode = EventOpcode.Start, Version = 2, Keywords = Keywords.ContentionKeyword)]
87+
private void ContentionStart(
88+
ContentionFlagsMap ContentionFlags,
89+
ushort ClrInstanceID,
90+
nint LockID,
91+
nint AssociatedObjectID,
92+
ulong LockOwnerThreadID)
93+
{
94+
Debug.Assert(IsEnabled(EventLevel.Informational, Keywords.ContentionKeyword));
95+
LogContentionStart(ContentionFlags, ClrInstanceID, LockID, AssociatedObjectID, LockOwnerThreadID);
96+
}
97+
98+
[Event(91, Level = EventLevel.Informational, Message = Messages.ContentionStop, Task = Tasks.Contention, Opcode = EventOpcode.Stop, Version = 1, Keywords = Keywords.ContentionKeyword)]
99+
private void ContentionStop(ContentionFlagsMap ContentionFlags, ushort ClrInstanceID, double DurationNs)
100+
{
101+
Debug.Assert(IsEnabled(EventLevel.Informational, Keywords.ContentionKeyword));
102+
LogContentionStop(ContentionFlags, ClrInstanceID, DurationNs);
103+
}
104+
105+
[NonEvent]
106+
[MethodImpl(MethodImplOptions.NoInlining)]
107+
public void ContentionStop(double durationNs) =>
108+
ContentionStop(ContentionFlagsMap.Managed, DefaultClrInstanceId, durationNs);
109+
72110
[Event(50, Level = EventLevel.Informational, Message = Messages.WorkerThread, Task = Tasks.ThreadPoolWorkerThread, Opcode = EventOpcode.Start, Version = 0, Keywords = Keywords.ThreadingKeyword)]
73111
public unsafe void ThreadPoolWorkerThreadStart(
74112
uint ActiveWorkerThreadCount,
@@ -184,7 +222,9 @@ public void ThreadPoolIOEnqueue(RegisteredWaitHandle registeredWaitHandle)
184222
{
185223
if (IsEnabled(EventLevel.Verbose, Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword))
186224
{
225+
#pragma warning disable CA1416 // 'RegisteredWaitHandle.Repeating' is unsupported on: 'browser'
187226
ThreadPoolIOEnqueue((IntPtr)registeredWaitHandle.GetHashCode(), IntPtr.Zero, registeredWaitHandle.Repeating);
227+
#pragma warning restore CA1416
188228
}
189229
}
190230

0 commit comments

Comments
 (0)