Update SOS for when the portable thread pool is used for IO completions on Windows...
authorKoundinya Veluri <kouvel@users.noreply.github.com>
Fri, 4 Mar 2022 21:42:57 +0000 (13:42 -0800)
committerGitHub <noreply@github.com>
Fri, 4 Mar 2022 21:42:57 +0000 (13:42 -0800)
- Depends on https://github.com/dotnet/runtime/pull/64834
- Refactored the enumeration of the global queue and reused it to also enumerate the high-priority work item queue
- Omitted info that is specific to the native thread pool implementation, when the portable thread pool is being used for IO completions

src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h

index eafce1b5e001466a8dcfc497b123f9d61ac3d360..03be99886f7c3af90cdaff92d296d2a846031a9d 100644 (file)
@@ -8668,6 +8668,7 @@ DECLARE_API(ThreadPool)
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     // Check whether the portable thread pool is being used and fill in the thread pool data
 
+    UINT64 ui64Value = 0;
     DacpObjectData vPortableTpHcLogArray;
     int portableTpHcLogEntry_tickCountOffset = 0;
     int portableTpHcLogEntry_stateOrTransitionOffset = 0;
@@ -8676,8 +8677,6 @@ DECLARE_API(ThreadPool)
     int portableTpHcLogEntry_lastHistoryMeanOffset = 0;
     do // while (false)
     {
-        UINT64 ui64Value = 0;
-
         // Determine if the portable thread pool is enabled
         if (FAILED(
                 GetNonSharedStaticFieldValueFromName(
@@ -8947,7 +8946,7 @@ DECLARE_API(ThreadPool)
         GetInfoFromName(corelibModule, "System.Threading.ThreadPoolWorkQueue+WorkStealingQueue", &threadPoolWorkStealingQueueMd);
 
         // Walk every heap item looking for the global queue and local queues.
-        ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item");
+        ExtOut("\nQueued work items:\n%" THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item");
         HeapStat stats;
         for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
         {
@@ -8960,86 +8959,31 @@ DECLARE_API(ThreadPool)
 
             if (mtdata.cl == threadPoolWorkQueueMd)
             {
-                // We found a global queue (there should be only one, given one AppDomain).
-                // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>.
-                int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems"));
+                // We found a ThreadPoolWorkQueue (there should be only one, given one AppDomain).
+
+                // Enumerate high-priority work items.
+                int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("highPriorityWorkItems"));
                 if (offset > 0)
                 {
                     DWORD_PTR workItemsConcurrentQueuePtr;
                     MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset);
                     if (sos::IsObject(workItemsConcurrentQueuePtr, false))
                     {
-                        // We got the ConcurrentQueue.  Get its head segment.
-                        sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
-                        offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
-                        if (offset > 0)
-                        {
-                            // Now, walk from segment to segment, each of which contains an array of work items.
-                            DWORD_PTR segmentPtr;
-                            MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
-                            while (sos::IsObject(segmentPtr, false))
-                            {
-                                sos::Object segment = TO_TADDR(segmentPtr);
-
-                                // Get the work items array.  It's an array of Slot structs, which starts with the T.
-                                offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
-                                if (offset <= 0)
-                                {
-                                    break;
-                                }
-
-                                DWORD_PTR slotsPtr;
-                                MOVE(slotsPtr, segment.GetAddress() + offset);
-                                if (!sos::IsObject(slotsPtr, false))
-                                {
-                                    break;
-                                }
-
-                                // Walk every element in the array, outputting details on non-null work items.
-                                DacpObjectData slotsArray;
-                                if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
-                                {
-                                    for (int i = 0; i < slotsArray.dwNumComponents; i++)
-                                    {
-                                        CLRDATA_ADDRESS workItemPtr;
-                                        MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot
-                                        if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
-                                        {
-                                            sos::Object workItem = TO_TADDR(workItemPtr);
-                                            stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
-                                            DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName());
-                                            if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
-                                                (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
-                                            {
-                                                CLRDATA_ADDRESS delegatePtr;
-                                                MOVE(delegatePtr, workItem.GetAddress() + offset);
-                                                CLRDATA_ADDRESS md;
-                                                if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
-                                                {
-                                                    NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
-                                                    ExtOut(" => %S", g_mdName);
-                                                }
-                                            }
-                                            ExtOut("\n");
-                                        }
-                                    }
-                                }
-
-                                // Move to the next segment.
-                                DacpFieldDescData segmentField;
-                                offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
-                                if (offset <= 0)
-                                {
-                                    break;
-                                }
+                        // We got the ConcurrentQueue.  Enumerate it.
+                        EnumerateThreadPoolGlobalWorkItemConcurrentQueue(workItemsConcurrentQueuePtr, "[Global high-pri]", &stats);
+                    }
+                }
 
-                                MOVE(segmentPtr, segment.GetAddress() + offset);
-                                if (segmentPtr == NULL)
-                                {
-                                    break;
-                                }
-                            }
-                        }
+                // Enumerate normal-priority work items.
+                offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems"));
+                if (offset > 0)
+                {
+                    DWORD_PTR workItemsConcurrentQueuePtr;
+                    MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset);
+                    if (sos::IsObject(workItemsConcurrentQueuePtr, false))
+                    {
+                        // We got the ConcurrentQueue.  Enumerate it.
+                        EnumerateThreadPoolGlobalWorkItemConcurrentQueue(workItemsConcurrentQueuePtr, "[Global]", &stats);
                     }
                 }
             }
@@ -9063,7 +9007,7 @@ DECLARE_API(ThreadPool)
                             {
                                 sos::Object workItem = TO_TADDR(workItemPtr);
                                 stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
-                                DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName());
+                                DMLOut("%" THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName());
                                 if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
                                     (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
                                 {
@@ -9219,14 +9163,26 @@ DECLARE_API(ThreadPool)
     ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
     ExtOut ("--------------------------------------\n");
 
-    ExtOut ("Completion Port Thread:");
-    ExtOut ("Total: %d", threadpool.NumCPThreads);
-    ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
-    ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
-    ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
-    ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
-    ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
-    ExtOut ("\n");
+    // Determine if the portable thread pool is being used for IO. The portable thread pool does not use a separate set of
+    // threads for processing IO completions.
+    if (FAILED(
+            GetNonSharedStaticFieldValueFromName(
+                &ui64Value,
+                corelibModule,
+                "System.Threading.ThreadPool",
+                W("UsePortableThreadPoolForIO"),
+                ELEMENT_TYPE_BOOLEAN)) ||
+        ui64Value == 0)
+    {
+        ExtOut ("Completion Port Thread:");
+        ExtOut ("Total: %d", threadpool.NumCPThreads);
+        ExtOut (" Free: %d", threadpool.NumFreeCPThreads);
+        ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);
+        ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
+        ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
+        ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);
+        ExtOut ("\n");
+    }
 
     return Status;
 }
index ff722fb0e0937be8624ab97b27aee0b4511ed091..2e1ae1f8c9b46ec0bf22f478c5d35f96919f1c6c 100644 (file)
@@ -5975,3 +5975,88 @@ HRESULT GetMetadataMemory(CLRDATA_ADDRESS address, ULONG32 bufferSize, BYTE* buf
 }
 
 #endif // FEATURE_PAL
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Miscellaneous helper methods
+//
+
+void EnumerateThreadPoolGlobalWorkItemConcurrentQueue(
+    DWORD_PTR workItemsConcurrentQueuePtr,
+    const char *queueName,
+    HeapStat *stats)
+{
+    // Get its head segment.
+    sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
+    int offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
+    if (offset <= 0)
+    {
+        return;
+    }
+
+    // Now, walk from segment to segment, each of which contains an array of work items.
+    DWORD_PTR segmentPtr;
+    MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
+    while (sos::IsObject(segmentPtr, false))
+    {
+        sos::Object segment = TO_TADDR(segmentPtr);
+
+        // Get the work items array.  It's an array of Slot structs, which starts with the T.
+        offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
+        if (offset <= 0)
+        {
+            break;
+        }
+
+        DWORD_PTR slotsPtr;
+        MOVE(slotsPtr, segment.GetAddress() + offset);
+        if (!sos::IsObject(slotsPtr, false))
+        {
+            break;
+        }
+
+        // Walk every element in the array, outputting details on non-null work items.
+        DacpObjectData slotsArray;
+        if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
+        {
+            for (int i = 0; i < slotsArray.dwNumComponents; i++)
+            {
+                CLRDATA_ADDRESS workItemPtr;
+                MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot
+                if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
+                {
+                    sos::Object workItem = TO_TADDR(workItemPtr);
+                    stats->Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
+                    DMLOut("%" THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "s %s %S", queueName, DMLObject(workItem.GetAddress()), workItem.GetTypeName());
+                    if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
+                        (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
+                    {
+                        CLRDATA_ADDRESS delegatePtr;
+                        MOVE(delegatePtr, workItem.GetAddress() + offset);
+                        CLRDATA_ADDRESS md;
+                        if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
+                        {
+                            NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
+                            ExtOut(" => %S", g_mdName);
+                        }
+                    }
+                    ExtOut("\n");
+                }
+            }
+        }
+
+        // Move to the next segment.
+        DacpFieldDescData segmentField;
+        offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
+        if (offset <= 0)
+        {
+            break;
+        }
+
+        MOVE(segmentPtr, segment.GetAddress() + offset);
+        if (segmentPtr == NULL)
+        {
+            break;
+        }
+    }
+}
index 5b252727052289395b55e1a5f2e2b4e720925e4e..e8a5d36fb64061ed991d6bccd65520b514e4a36c 100644 (file)
@@ -3277,4 +3277,16 @@ private:
     HRESULT PrintCurrentInternalFrame();
 };
 #include "sigparser.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+// Miscellaneous helper methods
+//
+
+#define THREAD_POOL_WORK_ITEM_TABLE_QUEUE_WIDTH "17"
+void EnumerateThreadPoolGlobalWorkItemConcurrentQueue(
+    DWORD_PTR workItemsConcurrentQueuePtr,
+    const char *queueName,
+    HeapStat *stats);
+
 #endif // __util_h__