////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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;
int portableTpHcLogEntry_lastHistoryMeanOffset = 0;
do // while (false)
{
- UINT64 ui64Value = 0;
-
// Determine if the portable thread pool is enabled
if (FAILED(
GetNonSharedStaticFieldValueFromName(
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)
{
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);
}
}
}
{
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)
{
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;
}
}
#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;
+ }
+ }
+}