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 + + + + + +