diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 24c4b35fb24455..052f8cf6c2a6e2 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -126,6 +126,7 @@
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.CoreCLR.cs
new file mode 100644
index 00000000000000..e99c225beadc07
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.CoreCLR.cs
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading;
+using System.Diagnostics.Tracing;
+using System.Runtime.CompilerServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System.Diagnostics.Tracing
+{
+ // This is part of the NativeRuntimeEventsource, which is the managed version of the Microsoft-Windows-DotNETRuntime provider.
+ // It contains the handwritten implementation of the ThreadPool events.
+ // The events here do not call into the typical WriteEvent* APIs unlike most EventSources because that results in the
+ // events to be forwarded to EventListeners twice, once directly from the managed WriteEvent API, and another time
+ // from the mechanism in NativeRuntimeEventSource.ProcessEvents that forwards native runtime events to EventListeners.
+ // To prevent this, these events call directly into QCalls provided by the runtime (refer to NativeRuntimeEventSource.cs) which call
+ // FireEtw* methods auto-generated from ClrEtwAll.man. This ensures that corresponding event sinks are being used
+ // for the native platform. Refer to src/coreclr/vm/nativeruntimesource.cpp.
+ // For Mono implementation of these events, refer to NativeRuntimeEventSource.PortableThreadPool.cs.
+ internal sealed partial class NativeRuntimeEventSource : EventSource
+ {
+ // This value does not seem to be used, leaving it as zero for now. It may be useful for a scenario that may involve
+ // multiple instances of the runtime within the same process, but then it seems unlikely that both instances' thread
+ // pools would be in moderate use.
+ private const ushort DefaultClrInstanceId = 0;
+
+ private static class Messages
+ {
+ public const string WorkerThread = "ActiveWorkerThreadCount={0};\nRetiredWorkerThreadCount={1};\nClrInstanceID={2}";
+ public const string WorkerThreadAdjustmentSample = "Throughput={0};\nClrInstanceID={1}";
+ public const string WorkerThreadAdjustmentAdjustment = "AverageThroughput={0};\nNewWorkerThreadCount={1};\nReason={2};\nClrInstanceID={3}";
+ public const string WorkerThreadAdjustmentStats = "Duration={0};\nThroughput={1};\nThreadWave={2};\nThroughputWave={3};\nThroughputErrorEstimate={4};\nAverageThroughputErrorEstimate={5};\nThroughputRatio={6};\nConfidence={7};\nNewControlSetting={8};\nNewThreadWaveMagnitude={9};\nClrInstanceID={10}";
+ public const string IOEnqueue = "NativeOverlapped={0};\nOverlapped={1};\nMultiDequeues={2};\nClrInstanceID={3}";
+ public const string IO = "NativeOverlapped={0};\nOverlapped={1};\nClrInstanceID={2}";
+ public const string WorkingThreadCount = "Count={0};\nClrInstanceID={1}";
+ }
+
+ // The task definitions for the ETW manifest
+ public static class Tasks // this name and visibility is important for EventSource
+ {
+ public const EventTask ThreadPoolWorkerThread = (EventTask)16;
+ public const EventTask ThreadPoolWorkerThreadAdjustment = (EventTask)18;
+ public const EventTask ThreadPool = (EventTask)23;
+ public const EventTask ThreadPoolWorkingThreadCount = (EventTask)22;
+ }
+
+ public static class Opcodes // this name and visibility is important for EventSource
+ {
+ public const EventOpcode IOEnqueue = (EventOpcode)13;
+ public const EventOpcode IODequeue = (EventOpcode)14;
+ public const EventOpcode Wait = (EventOpcode)90;
+ public const EventOpcode Sample = (EventOpcode)100;
+ public const EventOpcode Adjustment = (EventOpcode)101;
+ public const EventOpcode Stats = (EventOpcode)102;
+ }
+
+ public enum ThreadAdjustmentReasonMap : uint
+ {
+ Warmup,
+ Initializing,
+ RandomMove,
+ ClimbingMove,
+ ChangePoint,
+ Stabilizing,
+ Starvation,
+ ThreadTimedOut
+ }
+
+ [Event(50, Level = EventLevel.Informational, Message = Messages.WorkerThread, Task = Tasks.ThreadPoolWorkerThread, Opcode = EventOpcode.Start, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolWorkerThreadStart(
+ uint ActiveWorkerThreadCount,
+ uint RetiredWorkerThreadCount = 0,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ LogThreadPoolWorkerThreadStart(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ }
+ }
+
+ [Event(51, Level = EventLevel.Informational, Message = Messages.WorkerThread, Task = Tasks.ThreadPoolWorkerThread, Opcode = EventOpcode.Stop, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public void ThreadPoolWorkerThreadStop(
+ uint ActiveWorkerThreadCount,
+ uint RetiredWorkerThreadCount = 0,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ LogThreadPoolWorkerThreadStop(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ }
+ }
+
+ [Event(57, Level = EventLevel.Informational, Message = Messages.WorkerThread, Task = Tasks.ThreadPoolWorkerThread, Opcode = Opcodes.Wait, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void ThreadPoolWorkerThreadWait(
+ uint ActiveWorkerThreadCount,
+ uint RetiredWorkerThreadCount = 0,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ LogThreadPoolWorkerThreadWait(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ }
+ }
+
+ [Event(54, Level = EventLevel.Informational, Message = Messages.WorkerThreadAdjustmentSample, Task = Tasks.ThreadPoolWorkerThreadAdjustment, Opcode = Opcodes.Sample, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolWorkerThreadAdjustmentSample(
+ double Throughput,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (!IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ return;
+ }
+ LogThreadPoolWorkerThreadAdjustmentSample(Throughput, ClrInstanceID);
+ }
+
+ [Event(55, Level = EventLevel.Informational, Message = Messages.WorkerThreadAdjustmentAdjustment, Task = Tasks.ThreadPoolWorkerThreadAdjustment, Opcode = Opcodes.Adjustment, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolWorkerThreadAdjustmentAdjustment(
+ double AverageThroughput,
+ uint NewWorkerThreadCount,
+ ThreadAdjustmentReasonMap Reason,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (!IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ return;
+ }
+ LogThreadPoolWorkerThreadAdjustmentAdjustment(AverageThroughput, NewWorkerThreadCount, Reason, ClrInstanceID);
+ }
+
+ [Event(56, Level = EventLevel.Verbose, Message = Messages.WorkerThreadAdjustmentStats, Task = Tasks.ThreadPoolWorkerThreadAdjustment, Opcode = Opcodes.Stats, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolWorkerThreadAdjustmentStats(
+ double Duration,
+ double Throughput,
+ double ThreadWave,
+ double ThroughputWave,
+ double ThroughputErrorEstimate,
+ double AverageThroughputErrorEstimate,
+ double ThroughputRatio,
+ double Confidence,
+ double NewControlSetting,
+ ushort NewThreadWaveMagnitude,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (!IsEnabled(EventLevel.Verbose, Keywords.ThreadingKeyword))
+ {
+ return;
+ }
+ LogThreadPoolWorkerThreadAdjustmentStats(Duration, Throughput, ThreadWave, ThroughputWave, ThroughputErrorEstimate, AverageThroughputErrorEstimate, ThroughputRatio, Confidence, NewControlSetting, NewThreadWaveMagnitude, ClrInstanceID);
+ }
+
+ [Event(63, Level = EventLevel.Verbose, Message = Messages.IOEnqueue, Task = Tasks.ThreadPool, Opcode = Opcodes.IOEnqueue, Version = 0, Keywords = Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword)]
+ private unsafe void ThreadPoolIOEnqueue(
+ IntPtr NativeOverlapped,
+ IntPtr Overlapped,
+ bool MultiDequeues,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ int multiDequeuesInt = Convert.ToInt32(MultiDequeues); // bool maps to "win:Boolean", a 4-byte boolean
+ LogThreadPoolIOEnqueue(NativeOverlapped, Overlapped, MultiDequeues, ClrInstanceID);
+ }
+
+ // TODO: This event is fired for minor compat with CoreCLR in this case. Consider removing this method and use
+ // FrameworkEventSource's thread transfer send/receive events instead at callers.
+ [NonEvent]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void ThreadPoolIOEnqueue(RegisteredWaitHandle registeredWaitHandle)
+ {
+ if (IsEnabled(EventLevel.Verbose, Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword))
+ {
+ ThreadPoolIOEnqueue((IntPtr)registeredWaitHandle.GetHashCode(), IntPtr.Zero, registeredWaitHandle.Repeating);
+ }
+ }
+
+ [Event(64, Level = EventLevel.Verbose, Message = Messages.IO, Task = Tasks.ThreadPool, Opcode = Opcodes.IODequeue, Version = 0, Keywords = Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword)]
+ private unsafe void ThreadPoolIODequeue(
+ IntPtr NativeOverlapped,
+ IntPtr Overlapped,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ LogThreadPoolIODequeue(NativeOverlapped, Overlapped, ClrInstanceID);
+ }
+
+ // TODO: This event is fired for minor compat with CoreCLR in this case. Consider removing this method and use
+ // FrameworkEventSource's thread transfer send/receive events instead at callers.
+ [NonEvent]
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void ThreadPoolIODequeue(RegisteredWaitHandle registeredWaitHandle)
+ {
+ if (IsEnabled(EventLevel.Verbose, Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword))
+ {
+ ThreadPoolIODequeue((IntPtr)registeredWaitHandle.GetHashCode(), IntPtr.Zero);
+ }
+ }
+
+ [Event(60, Level = EventLevel.Verbose, Message = Messages.WorkingThreadCount, Task = Tasks.ThreadPoolWorkingThreadCount, Opcode = EventOpcode.Start, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolWorkingThreadCount(uint Count, ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (!IsEnabled(EventLevel.Verbose, Keywords.ThreadingKeyword))
+ {
+ return;
+ }
+ LogThreadPoolWorkingThreadCount(Count, ClrInstanceID);
+ }
+ }
+}
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 64d1b07c6f9122..b21f78edc6062a 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -989,13 +989,15 @@ FCFuncStart(gStreamFuncs)
FCFuncEnd()
-#if defined(FEATURE_EVENTSOURCE_XPLAT) || defined(FEATURE_PERFTRACING)
-FCFuncStart(gEventLogger)
#if defined(FEATURE_EVENTSOURCE_XPLAT)
+FCFuncStart(gEventLogger)
QCFuncElement("IsEventSourceLoggingEnabled", XplatEventSourceLogger::IsEventSourceLoggingEnabled)
QCFuncElement("LogEventSource", XplatEventSourceLogger::LogEventSource)
+FCFuncEnd()
#endif // defined(FEATURE_EVENTSOURCE_XPLAT)
+
#if defined(FEATURE_PERFTRACING)
+FCFuncStart(gNativeEventLogger)
QCFuncElement("LogThreadPoolWorkerThreadStart", NativeEventLogger::LogThreadPoolWorkerThreadStart)
QCFuncElement("LogThreadPoolWorkerThreadStop", NativeEventLogger::LogThreadPoolWorkerThreadStop)
QCFuncElement("LogThreadPoolWorkerThreadWait", NativeEventLogger::LogThreadPoolWorkerThreadWait)
@@ -1005,9 +1007,8 @@ FCFuncStart(gEventLogger)
QCFuncElement("LogThreadPoolIOEnqueue", NativeEventLogger::LogThreadPoolIOEnqueue)
QCFuncElement("LogThreadPoolIODequeue", NativeEventLogger::LogThreadPoolIODequeue)
QCFuncElement("LogThreadPoolWorkingThreadCount", NativeEventLogger::LogThreadPoolWorkingThreadCount)
-#endif // defined(FEATURE_PERFTRACING)
FCFuncEnd()
-#endif // defined(FEATURE_EVENTSOURCE_XPLAT) || defined(FEATURE_PERFTRACING)
+#endif // defined(FEATURE_PERFTRACING)
#ifdef FEATURE_PERFTRACING
FCFuncStart(gEventPipeInternalFuncs)
@@ -1165,6 +1166,9 @@ FCClassElement("ModuleBuilder", "System.Reflection.Emit", gCOMModuleBuilderFuncs
FCClassElement("ModuleHandle", "System", gCOMModuleHandleFuncs)
FCClassElement("Monitor", "System.Threading", gMonitorFuncs)
FCClassElement("NativeLibrary", "System.Runtime.InteropServices", gInteropNativeLibraryFuncs)
+#if defined(FEATURE_PERFTRACING)
+FCClassElement("NativeRuntimeEventSource", "System.Diagnostics.Tracing", gNativeEventLogger)
+#endif //defined(FEATURE_PERFTRACING)
#ifdef FEATURE_COMINTEROP
FCClassElement("OAVariantLib", "Microsoft.Win32", gOAVariantFuncs)
#endif
@@ -1213,7 +1217,7 @@ FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs)
FCClassElement("X86Base", "System.Runtime.Intrinsics.X86", gX86BaseFuncs)
#endif // defined(TARGET_X86) || defined(TARGET_AMD64)
-#if defined(FEATURE_EVENTSOURCE_XPLAT) || defined(FEATURE_PERFTRACING)
+#if defined(FEATURE_EVENTSOURCE_XPLAT)
FCClassElement("XplatEventLogger", "System.Diagnostics.Tracing", gEventLogger)
#endif //defined(FEATURE_EVENTSOURCE_XPLAT)
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 01918eedac5644..8c9d4d651f37d0 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -1960,7 +1960,7 @@
-
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
index 020ca17387f2dc..d8bf7dcad1cc3a 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs
@@ -1283,7 +1283,15 @@ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* rel
#endif // FEATURE_MANAGED_ETW
if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
- WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data);
+ {
+#if MONO && !TARGET_BROWSER
+ // On Mono, managed events from NativeRuntimeEventSource are written using WriteEventCore which can be
+ // written doubly because EventPipe tries to pump it back up to EventListener via NativeRuntimeEventSource.ProcessEvents.
+ // So we need to prevent this from getting written directly to the Listeners.
+ if (this.GetType() != typeof(NativeRuntimeEventSource))
+#endif // MONO && !TARGET_BROWSER
+ WriteToAllListeners(eventId, pActivityId, relatedActivityId, eventDataCount, data);
+ }
}
catch (Exception ex)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs
index 5884e3b45f53ce..b15be430a9de58 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs
@@ -8,14 +8,9 @@
namespace System.Diagnostics.Tracing
{
- // This is part of the NativeRuntimeEventsource, which is the managed version of the Microsoft-Windows-DotNETRuntime provider.
- // It contains the handwritten implementation of the ThreadPool events.
- // The events here do not call into the typical WriteEvent* APIs unlike most EventSources because that results in the
- // events to be forwarded to EventListeners twice, once directly from the managed WriteEvent API, and another time
- // from the mechanism in NativeRuntimeEventSource.ProcessEvents that forwards native runtime events to EventListeners.
- // To prevent this, these events call directly into QCalls provided by the runtime (refer to NativeRuntimeEventSource.cs) which call
- // FireEtw* methods auto-generated from ClrEtwAll.man. This ensures that corresponding event sinks are being used
- // for the native platform. Refer to src/coreclr/vm/nativeruntimesource.cpp.
+ // This is part of the NativeRuntimeEventsource for Mono, which is the managed version of the Microsoft-Windows-DotNETRuntime provider.
+ // and contains the implementation of ThreadPool events.
+ // To look at CoreCLR implementation of these events, refer to NativeRuntimeEventSource.PortableThreadPool.CoreCLR.cs.
internal sealed partial class NativeRuntimeEventSource : EventSource
{
// This value does not seem to be used, leaving it as zero for now. It may be useful for a scenario that may involve
@@ -65,6 +60,25 @@ public enum ThreadAdjustmentReasonMap : uint
ThreadTimedOut
}
+ [NonEvent]
+ private unsafe void WriteThreadEvent(int eventId, uint numExistingThreads)
+ {
+ uint retiredWorkerThreadCount = 0;
+ ushort clrInstanceId = DefaultClrInstanceId;
+
+ EventData* data = stackalloc EventData[3];
+ data[0].DataPointer = (IntPtr)(&numExistingThreads);
+ data[0].Size = sizeof(uint);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&retiredWorkerThreadCount);
+ data[1].Size = sizeof(uint);
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&clrInstanceId);
+ data[2].Size = sizeof(ushort);
+ data[2].Reserved = 0;
+ WriteEventCore(eventId, 3, data);
+ }
+
[Event(50, Level = EventLevel.Informational, Message = Messages.WorkerThread, Task = Tasks.ThreadPoolWorkerThread, Opcode = EventOpcode.Start, Version = 0, Keywords = Keywords.ThreadingKeyword)]
public unsafe void ThreadPoolWorkerThreadStart(
uint ActiveWorkerThreadCount,
@@ -73,7 +87,7 @@ public unsafe void ThreadPoolWorkerThreadStart(
{
if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
{
- LogThreadPoolWorkerThreadStart(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ WriteThreadEvent(50, ActiveWorkerThreadCount);
}
}
@@ -85,7 +99,7 @@ public void ThreadPoolWorkerThreadStop(
{
if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
{
- LogThreadPoolWorkerThreadStop(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ WriteThreadEvent(51, ActiveWorkerThreadCount);
}
}
@@ -98,7 +112,7 @@ public void ThreadPoolWorkerThreadWait(
{
if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
{
- LogThreadPoolWorkerThreadWait(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID);
+ WriteThreadEvent(57, ActiveWorkerThreadCount);
}
}
@@ -111,7 +125,14 @@ public unsafe void ThreadPoolWorkerThreadAdjustmentSample(
{
return;
}
- LogThreadPoolWorkerThreadAdjustmentSample(Throughput, ClrInstanceID);
+ EventData* data = stackalloc EventData[2];
+ data[0].DataPointer = (IntPtr)(&Throughput);
+ data[0].Size = sizeof(double);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[1].Size = sizeof(ushort);
+ data[1].Reserved = 0;
+ WriteEventCore(54, 2, data);
}
[Event(55, Level = EventLevel.Informational, Message = Messages.WorkerThreadAdjustmentAdjustment, Task = Tasks.ThreadPoolWorkerThreadAdjustment, Opcode = Opcodes.Adjustment, Version = 0, Keywords = Keywords.ThreadingKeyword)]
@@ -125,7 +146,20 @@ public unsafe void ThreadPoolWorkerThreadAdjustmentAdjustment(
{
return;
}
- LogThreadPoolWorkerThreadAdjustmentAdjustment(AverageThroughput, NewWorkerThreadCount, Reason, ClrInstanceID);
+ EventData* data = stackalloc EventData[4];
+ data[0].DataPointer = (IntPtr)(&AverageThroughput);
+ data[0].Size = sizeof(double);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&NewWorkerThreadCount);
+ data[1].Size = sizeof(uint);
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&Reason);
+ data[2].Size = sizeof(ThreadAdjustmentReasonMap);
+ data[2].Reserved = 0;
+ data[3].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[3].Size = sizeof(ushort);
+ data[3].Reserved = 0;
+ WriteEventCore(55, 4, data);
}
[Event(56, Level = EventLevel.Verbose, Message = Messages.WorkerThreadAdjustmentStats, Task = Tasks.ThreadPoolWorkerThreadAdjustment, Opcode = Opcodes.Stats, Version = 0, Keywords = Keywords.ThreadingKeyword)]
@@ -146,7 +180,41 @@ public unsafe void ThreadPoolWorkerThreadAdjustmentStats(
{
return;
}
- LogThreadPoolWorkerThreadAdjustmentStats(Duration, Throughput, ThreadWave, ThroughputWave, ThroughputErrorEstimate, AverageThroughputErrorEstimate, ThroughputRatio, Confidence, NewControlSetting, NewThreadWaveMagnitude, ClrInstanceID);
+ EventData* data = stackalloc EventData[11];
+ data[0].DataPointer = (IntPtr)(&Duration);
+ data[0].Size = sizeof(double);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&Throughput);
+ data[1].Size = sizeof(double);
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&ThreadWave);
+ data[2].Size = sizeof(double);
+ data[2].Reserved = 0;
+ data[3].DataPointer = (IntPtr)(&ThroughputWave);
+ data[3].Size = sizeof(double);
+ data[3].Reserved = 0;
+ data[4].DataPointer = (IntPtr)(&ThroughputErrorEstimate);
+ data[4].Size = sizeof(double);
+ data[4].Reserved = 0;
+ data[5].DataPointer = (IntPtr)(&AverageThroughputErrorEstimate);
+ data[5].Size = sizeof(double);
+ data[5].Reserved = 0;
+ data[6].DataPointer = (IntPtr)(&ThroughputRatio);
+ data[6].Size = sizeof(double);
+ data[6].Reserved = 0;
+ data[7].DataPointer = (IntPtr)(&Confidence);
+ data[7].Size = sizeof(double);
+ data[7].Reserved = 0;
+ data[8].DataPointer = (IntPtr)(&NewControlSetting);
+ data[8].Size = sizeof(double);
+ data[8].Reserved = 0;
+ data[9].DataPointer = (IntPtr)(&NewThreadWaveMagnitude);
+ data[9].Size = sizeof(ushort);
+ data[9].Reserved = 0;
+ data[10].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[10].Size = sizeof(ushort);
+ data[10].Reserved = 0;
+ WriteEventCore(56, 11, data);
}
[Event(63, Level = EventLevel.Verbose, Message = Messages.IOEnqueue, Task = Tasks.ThreadPool, Opcode = Opcodes.IOEnqueue, Version = 0, Keywords = Keywords.ThreadingKeyword | Keywords.ThreadTransferKeyword)]
@@ -157,7 +225,20 @@ private unsafe void ThreadPoolIOEnqueue(
ushort ClrInstanceID = DefaultClrInstanceId)
{
int multiDequeuesInt = Convert.ToInt32(MultiDequeues); // bool maps to "win:Boolean", a 4-byte boolean
- LogThreadPoolIOEnqueue(NativeOverlapped, Overlapped, MultiDequeues, ClrInstanceID);
+ EventData* data = stackalloc EventData[4];
+ data[0].DataPointer = (IntPtr)(&NativeOverlapped);
+ data[0].Size = IntPtr.Size;
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&Overlapped);
+ data[1].Size = IntPtr.Size;
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&multiDequeuesInt);
+ data[2].Size = sizeof(int);
+ data[2].Reserved = 0;
+ data[3].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[3].Size = sizeof(ushort);
+ data[3].Reserved = 0;
+ WriteEventCore(63, 4, data);
}
// TODO: This event is fired for minor compat with CoreCLR in this case. Consider removing this method and use
@@ -178,7 +259,17 @@ private unsafe void ThreadPoolIODequeue(
IntPtr Overlapped,
ushort ClrInstanceID = DefaultClrInstanceId)
{
- LogThreadPoolIODequeue(NativeOverlapped, Overlapped, ClrInstanceID);
+ EventData* data = stackalloc EventData[3];
+ data[0].DataPointer = (IntPtr)(&NativeOverlapped);
+ data[0].Size = IntPtr.Size;
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&Overlapped);
+ data[1].Size = IntPtr.Size;
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[2].Size = sizeof(ushort);
+ data[2].Reserved = 0;
+ WriteEventCore(64, 3, data);
}
// TODO: This event is fired for minor compat with CoreCLR in this case. Consider removing this method and use
@@ -200,7 +291,14 @@ public unsafe void ThreadPoolWorkingThreadCount(uint Count, ushort ClrInstanceID
{
return;
}
- LogThreadPoolWorkingThreadCount(Count, ClrInstanceID);
+ EventData* data = stackalloc EventData[2];
+ data[0].DataPointer = (IntPtr)(&Count);
+ data[0].Size = sizeof(uint);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[1].Size = sizeof(ushort);
+ data[1].Reserved = 0;
+ WriteEventCore(60, 2, data);
}
}
}
diff --git a/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.cs b/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.cs
new file mode 100644
index 00000000000000..08c7f57a38142e
--- /dev/null
+++ b/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.cs
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics.Tracing;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using Microsoft.Diagnostics.Tracing;
+using Tracing.Tests.Common;
+
+namespace Tracing.Tests.GCFinalizers
+{
+ public class ProviderValidation
+ {
+ public static int Main(string[] args)
+ {
+ var providers = new List()
+ {
+ new Provider("Microsoft-DotNETCore-SampleProfiler"),
+ //ThreadingKeyword (0x10000)
+ new Provider("Microsoft-Windows-DotNETRuntime", 0x10000, EventLevel.Verbose)
+ };
+
+ var configuration = new SessionConfiguration(circularBufferSizeMB: 1024, format: EventPipeSerializationFormat.NetTrace, providers: providers);
+ return IpcTraceTest.RunAndValidateEventCounts(_expectedEventCounts, _eventGeneratingAction, configuration, _DoesTraceContainEvents);
+ }
+
+ private static Dictionary _expectedEventCounts = new Dictionary()
+ {
+ { "Microsoft-Windows-DotNETRuntime", -1 },
+ { "Microsoft-Windows-DotNETRuntimeRundown", -1 },
+ { "Microsoft-DotNETCore-SampleProfiler", -1 }
+ };
+
+ private static Action _eventGeneratingAction = () =>
+ {
+ Task[] tasks = new Task[50];
+ for (int i = 0; i < 50; i++)
+ {
+ tasks[i] = Task.Run(async () => {
+ long total = 0;
+ await Task.Delay(10);
+ var rnd = new Random();
+ for (int n = 1; n <= 100; n++)
+ total += rnd.Next(0, 100);
+ return total;
+ });
+ }
+ };
+
+ private static Func> _DoesTraceContainEvents = (source) =>
+ {
+ int threadPoolEventsCount = 0;
+ source.Clr.ThreadPoolWorkerThreadStart += (_) => threadPoolEventsCount += 1;
+ source.Clr.ThreadPoolWorkerThreadWait += (_) => threadPoolEventsCount += 1;
+ source.Clr.ThreadPoolWorkerThreadStop += (_) => threadPoolEventsCount += 1;
+ source.Clr.ThreadPoolWorkerThreadAdjustmentSample += (_) => threadPoolEventsCount += 1;
+ source.Clr.ThreadPoolWorkerThreadAdjustmentAdjustment += (_) => threadPoolEventsCount += 1;
+ return () => {
+ Logger.logger.Log("Event counts validation");
+ return threadPoolEventsCount >= 50 ? 100 : -1;
+ };
+ };
+ }
+}
diff --git a/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.csproj b/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.csproj
new file mode 100644
index 00000000000000..aec8e05b53cc70
--- /dev/null
+++ b/src/tests/tracing/eventpipe/eventsvalidation/ThreadPool.csproj
@@ -0,0 +1,16 @@
+
+
+ .NETCoreApp
+ exe
+ BuildAndRun
+ true
+ 1
+ true
+ true
+ true
+
+
+
+
+
+