From 41d9c1cc0dca16743a54971930e81d60777af746 Mon Sep 17 00:00:00 2001
From: Eduardo Velarde <32459232+eduardo-vp@users.noreply.github.com>
Date: Fri, 17 Jun 2022 10:56:23 -0700
Subject: [PATCH] Add event to capture min/max threads (#70063)
Event ThreadPoolMinMaxThreads added.
Parameters are:
ushort MinWorkerThreads
ushort MaxWorkerThreads
ushort MinIOCompletionThreads
ushort MaxIOCompletionThreads
ushort ClrInstanceID
It is fired in the ThreadPool constructor and in the SetMinThreads/SetMaxThreads functions.
---
...ource.PortableThreadPool.NativeSinks.CoreCLR.cs | 4 +
src/coreclr/gc/env/etmdummy.h | 1 +
src/coreclr/vm/ClrEtwAll.man | 35 +++++++-
src/coreclr/vm/nativeeventsource.cpp | 10 +++
src/coreclr/vm/nativeeventsource.h | 1 +
src/coreclr/vm/qcallentrypoints.cpp | 1 +
...meEventSource.PortableThreadPool.NativeSinks.cs | 17 ++++
.../NativeRuntimeEventSource.PortableThreadPool.cs | 39 +++++++++
.../src/System/Threading/PortableThreadPool.cs | 28 +++++++
.../tests/ThreadPoolTests.cs | 92 ++++++++++++++++++++++
...ntSource.PortableThreadPool.NativeSinks.Mono.cs | 4 +
src/mono/mono/component/event_pipe-stub.c | 20 +++++
src/mono/mono/component/event_pipe.c | 1 +
src/mono/mono/component/event_pipe.h | 9 +++
src/mono/mono/eventpipe/ep-rt-mono.c | 18 +++++
src/mono/mono/eventpipe/ep-rt-mono.h | 8 ++
src/mono/mono/eventpipe/gen-eventing-event-inc.lst | 1 +
src/mono/mono/metadata/icall-decl.h | 1 +
src/mono/mono/metadata/icall-def.h | 1 +
src/mono/mono/metadata/icall-eventpipe.c | 29 +++++++
20 files changed, 319 insertions(+), 1 deletion(-)
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.CoreCLR.cs
index f833134..cbf6e46 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Eventing/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.CoreCLR.cs
@@ -26,6 +26,10 @@ namespace System.Diagnostics.Tracing
[NonEvent]
[LibraryImport(RuntimeHelpers.QCall)]
+ internal static partial void LogThreadPoolMinMaxThreads(ushort MinWorkerThreads, ushort MaxWorkerThreads, ushort MinIOCompletionThreads, ushort MaxIOCompletionThreads, ushort ClrInstanceID);
+
+ [NonEvent]
+ [LibraryImport(RuntimeHelpers.QCall)]
internal static partial void LogThreadPoolWorkerThreadAdjustmentSample(double Throughput, ushort ClrInstanceID);
[NonEvent]
diff --git a/src/coreclr/gc/env/etmdummy.h b/src/coreclr/gc/env/etmdummy.h
index 5563721..575e306 100644
--- a/src/coreclr/gc/env/etmdummy.h
+++ b/src/coreclr/gc/env/etmdummy.h
@@ -78,6 +78,7 @@
#define FireEtwThreadPoolWorkerThreadAdjustmentAdjustment(AverageThroughput, NewWorkerThreadCount, Reason, ClrInstanceID) 0
#define FireEtwThreadPoolWorkerThreadAdjustmentStats(Duration, Throughput, ThreadWave, ThroughputWave, ThroughputErrorEstimate, AverageThroughputErrorEstimate, ThroughputRatio, Confidence, NewControlSetting, NewThreadWaveMagnitude, ClrInstanceID) 0
#define FireEtwThreadPoolWorkerThreadWait(ActiveWorkerThreadCount, RetiredWorkerThreadCount, ClrInstanceID) 0
+#define FireEtwThreadPoolMinMaxThreads (MinWorkerThreads, MaxWorkerThreads, MinIOCompletionThreads, MaxIOCompletionThreads, ClrInstanceID) 0
#define FireEtwThreadPoolWorkingThreadCount(Count, ClrInstanceID) 0
#define FireEtwThreadPoolEnqueue(WorkID, ClrInstanceID) 0
#define FireEtwThreadPoolDequeue(WorkID, ClrInstanceID) 0
diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man
index a55dec6..761d650 100644
--- a/src/coreclr/vm/ClrEtwAll.man
+++ b/src/coreclr/vm/ClrEtwAll.man
@@ -446,7 +446,13 @@
-
+
+
+
+
+
@@ -1579,6 +1585,24 @@
+
+
+
+
+
+
+
+
+
+ %1
+ %2
+ %3
+ %4
+ %5
+
+
+
+
@@ -3455,6 +3479,13 @@
keywords="ThreadingKeyword" task="YieldProcessorMeasurement" opcode="win:Info"
symbol="YieldProcessorMeasurement" message="$(string.RuntimePublisher.YieldProcessorMeasurementEventMessage)"/>
+
+
+
@@ -8529,6 +8561,7 @@
+
diff --git a/src/coreclr/vm/nativeeventsource.cpp b/src/coreclr/vm/nativeeventsource.cpp
index f2a8d7f..9ec8310 100644
--- a/src/coreclr/vm/nativeeventsource.cpp
+++ b/src/coreclr/vm/nativeeventsource.cpp
@@ -74,6 +74,16 @@ extern "C" void QCALLTYPE LogThreadPoolWorkerThreadWait(_In_z_ uint activeWorker
END_QCALL;
}
+extern "C" void QCALLTYPE LogThreadPoolMinMaxThreads(_In_z_ short minWorkerThreads, _In_z_ short maxWorkerThreads, _In_z_ short minIOCompletionThreads, _In_z_ short maxIOCompletionThreads, _In_z_ short clrInstanceID)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ FireEtwThreadPoolMinMaxThreads(minWorkerThreads, maxWorkerThreads, minIOCompletionThreads, maxIOCompletionThreads, clrInstanceID);
+
+ END_QCALL;
+}
+
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadAdjustmentSample(_In_z_ double throughput, _In_z_ short clrInstanceID)
{
QCALL_CONTRACT;
diff --git a/src/coreclr/vm/nativeeventsource.h b/src/coreclr/vm/nativeeventsource.h
index 6f89d55..c649f7d9 100644
--- a/src/coreclr/vm/nativeeventsource.h
+++ b/src/coreclr/vm/nativeeventsource.h
@@ -20,6 +20,7 @@ extern "C" BOOL QCALLTYPE IsEventSourceLoggingEnabled();
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadStart(_In_z_ uint activeWorkerThreadCount, _In_z_ uint retiredWorkerThreadCount, _In_z_ short clrInstanceID);
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadStop(_In_z_ uint activeWorkerThreadCount, _In_z_ uint retiredWorkerThreadCount, _In_z_ short clrInstanceID);
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadWait(_In_z_ uint activeWorkerThreadCount, _In_z_ uint retiredWorkerThreadCount, _In_z_ short clrInstanceID);
+extern "C" void QCALLTYPE LogThreadPoolMinMaxThreads(_In_z_ short minWorkerThreads, _In_z_ short maxWorkerThreads, _In_z_ short minIOCompletionThreads, _In_z_ short maxIOCompletionThreads, _In_z_ short clrInstanceID);
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadAdjustmentSample(_In_z_ double throughput, _In_z_ short clrInstanceID);
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadAdjustmentAdjustment(_In_z_ double averageThroughput, _In_z_ uint newWorkerThreadCount, _In_z_ uint reason, _In_z_ short clrInstanceID);
extern "C" void QCALLTYPE LogThreadPoolWorkerThreadAdjustmentStats(_In_z_ double duration, _In_z_ double throughput, _In_z_ double threadWave, _In_z_ double throughputWave, _In_z_ double throughputErrorEstimate, _In_z_ double AverageThroughputErrorEstimate, _In_z_ double ThroughputRatio, _In_z_ double confidence, _In_z_ double newControlSetting, _In_z_ short newThreadWaveMagnitude, _In_z_ short ClrInstanceID);
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 9f4851e..15c5fc5 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -275,6 +275,7 @@ static const Entry s_QCall[] =
DllImportEntry(LogThreadPoolWorkerThreadStart)
DllImportEntry(LogThreadPoolWorkerThreadStop)
DllImportEntry(LogThreadPoolWorkerThreadWait)
+ DllImportEntry(LogThreadPoolMinMaxThreads)
DllImportEntry(LogThreadPoolWorkerThreadAdjustmentSample)
DllImportEntry(LogThreadPoolWorkerThreadAdjustmentAdjustment)
DllImportEntry(LogThreadPoolWorkerThreadAdjustmentStats)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs
index 477d113..5e02261 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs
@@ -26,6 +26,7 @@ namespace System.Diagnostics.Tracing
private static class Messages
{
public const string WorkerThread = "ActiveWorkerThreadCount={0};\nRetiredWorkerThreadCount={1};\nClrInstanceID={2}";
+ public const string MinMaxThreads = "MinWorkerThreads={0};\nMaxWorkerThreads={1};\nMinIOCompletionThreads={2};\nMaxIOCompletionThreads={3};\nClrInstanceID={4}";
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}";
@@ -41,6 +42,7 @@ namespace System.Diagnostics.Tracing
public const EventTask ThreadPoolWorkerThreadAdjustment = (EventTask)18;
public const EventTask ThreadPool = (EventTask)23;
public const EventTask ThreadPoolWorkingThreadCount = (EventTask)22;
+ public const EventTask ThreadPoolMinMaxThreads = (EventTask)38;
}
public static class Opcodes // this name and visibility is important for EventSource
@@ -248,5 +250,20 @@ namespace System.Diagnostics.Tracing
{
LogThreadPoolIOPack(NativeOverlapped, Overlapped, ClrInstanceID);
}
+
+
+ [Event(59, Level = EventLevel.Informational, Message = Messages.MinMaxThreads, Task = Tasks.ThreadPoolMinMaxThreads, Opcode = EventOpcode.Info, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolMinMaxThreads(
+ ushort MinWorkerThreads,
+ ushort MaxWorkerThreads,
+ ushort MinIOCompletionThreads,
+ ushort MaxIOCompletionThreads,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ LogThreadPoolMinMaxThreads(MinWorkerThreads, MaxWorkerThreads, MinIOCompletionThreads, MaxIOCompletionThreads, ClrInstanceID);
+ }
+ }
}
}
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 6e4b13e..0031309 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
@@ -29,6 +29,7 @@ namespace System.Diagnostics.Tracing
private static class Messages
{
public const string WorkerThread = "ActiveWorkerThreadCount={0};\nRetiredWorkerThreadCount={1};\nClrInstanceID={2}";
+ public const string MinMaxThreads = "MinWorkerThreads={0};\nMaxWorkerThreads={1};\nMinIOCompletionThreads={2};\nMaxIOCompletionThreads={3};\nClrInstanceID={4}";
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}";
@@ -44,6 +45,7 @@ namespace System.Diagnostics.Tracing
public const EventTask ThreadPoolWorkerThreadAdjustment = (EventTask)18;
public const EventTask ThreadPool = (EventTask)23;
public const EventTask ThreadPoolWorkingThreadCount = (EventTask)22;
+ public const EventTask ThreadPoolMinMaxThreads = (EventTask)38;
}
public static class Opcodes // this name and visibility is important for EventSource
@@ -398,5 +400,42 @@ namespace System.Diagnostics.Tracing
data[2].Reserved = 0;
WriteEventCore(65, 3, data);
}
+
+
+#if !ES_BUILD_STANDALONE
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
+ Justification = EventSourceSuppressMessage)]
+#endif
+ [Event(59, Level = EventLevel.Informational, Message = Messages.MinMaxThreads, Task = Tasks.ThreadPoolMinMaxThreads, Opcode = EventOpcode.Info, Version = 0, Keywords = Keywords.ThreadingKeyword)]
+ public unsafe void ThreadPoolMinMaxThreads(
+ ushort MinWorkerThreads,
+ ushort MaxWorkerThreads,
+ ushort MinIOCompletionThreads,
+ ushort MaxIOCompletionThreads,
+ ushort ClrInstanceID = DefaultClrInstanceId)
+ {
+ if (!IsEnabled(EventLevel.Informational, Keywords.ThreadingKeyword))
+ {
+ return;
+ }
+ EventData* data = stackalloc EventData[5];
+ data[0].DataPointer = (IntPtr)(&MinWorkerThreads);
+ data[0].Size = sizeof(ushort);
+ data[0].Reserved = 0;
+ data[1].DataPointer = (IntPtr)(&MaxWorkerThreads);
+ data[1].Size = sizeof(ushort);
+ data[1].Reserved = 0;
+ data[2].DataPointer = (IntPtr)(&MinIOCompletionThreads);
+ data[2].Size = sizeof(ushort);
+ data[2].Reserved = 0;
+ data[3].DataPointer = (IntPtr)(&MaxIOCompletionThreads);
+ data[3].Size = sizeof(ushort);
+ data[3].Reserved = 0;
+ data[4].DataPointer = (IntPtr)(&ClrInstanceID);
+ data[4].Size = sizeof(ushort);
+ data[4].Reserved = 0;
+ WriteEventCore(59, 5, data);
+ }
+
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
index 92fbb2a..847a9e71 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Diagnostics.Tracing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -111,6 +112,15 @@ namespace System.Threading
_legacy_minIOCompletionThreads = 1;
_legacy_maxIOCompletionThreads = 1000;
+ if (NativeRuntimeEventSource.Log.IsEnabled())
+ {
+ NativeRuntimeEventSource.Log.ThreadPoolMinMaxThreads(
+ (ushort)_minThreads,
+ (ushort)_maxThreads,
+ (ushort)_legacy_minIOCompletionThreads,
+ (ushort)_legacy_maxIOCompletionThreads);
+ }
+
_separated.counts.NumThreadsGoal = _minThreads;
#if TARGET_WINDOWS
@@ -186,6 +196,15 @@ namespace System.Threading
addWorker = true;
}
}
+
+ if (NativeRuntimeEventSource.Log.IsEnabled())
+ {
+ NativeRuntimeEventSource.Log.ThreadPoolMinMaxThreads(
+ (ushort)_minThreads,
+ (ushort)_maxThreads,
+ (ushort)_legacy_minIOCompletionThreads,
+ (ushort)_legacy_maxIOCompletionThreads);
+ }
}
finally
{
@@ -256,6 +275,15 @@ namespace System.Threading
{
_separated.counts.InterlockedSetNumThreadsGoal(newMaxThreads);
}
+
+ if (NativeRuntimeEventSource.Log.IsEnabled())
+ {
+ NativeRuntimeEventSource.Log.ThreadPoolMinMaxThreads(
+ (ushort)_minThreads,
+ (ushort)_maxThreads,
+ (ushort)_legacy_minIOCompletionThreads,
+ (ushort)_legacy_maxIOCompletionThreads);
+ }
return true;
}
finally
diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs
index 9c6b06e..f0017ea 100644
--- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs
+++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.Tracing;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -1063,6 +1064,97 @@ namespace System.Threading.ThreadPools.Tests
}).Dispose();
}
+ private class ClrMinMaxThreadsEventListener : EventListener
+ {
+ private const string ClrProviderName = "Microsoft-Windows-DotNETRuntime";
+ private const EventKeywords ThreadingKeyword = (EventKeywords)0x10000;
+ private const int ThreadPoolMinMaxThreadsEventId = 59;
+
+ private readonly int _expectedEventCount;
+
+ public List