@@ -4518,8 +4518,7 @@ DECLARE_API(DumpAsync)
4518
4518
int offset = GetObjFieldOffset(ar.Address, ar.MT, W("m_stateFlags"), TRUE, &stateFlagsField);
4519
4519
if (offset != 0)
4520
4520
{
4521
- sos::Object obj = TO_TADDR(ar.Address);
4522
- MOVE(ar.TaskStateFlags, obj.GetAddress() + offset);
4521
+ MOVE(ar.TaskStateFlags, ar.Address + offset);
4523
4522
}
4524
4523
4525
4524
// Get the async state machine object's StateMachine field.
@@ -8161,18 +8160,24 @@ DECLARE_API(ThreadPool)
8161
8160
8162
8161
if ((Status = threadpool.Request(g_sos)) == S_OK)
8163
8162
{
8164
- BOOL doHCDump = FALSE;
8163
+ BOOL doHCDump = FALSE, doWorkItemDump = FALSE, dml = FALSE ;
8165
8164
8166
8165
CMDOption option[] =
8167
8166
{ // name, vptr, type, hasValue
8168
- {"-ti", &doHCDump, COBOOL, FALSE}
8167
+ {"-ti", &doHCDump, COBOOL, FALSE},
8168
+ {"-wi", &doWorkItemDump, COBOOL, FALSE},
8169
+ #ifndef FEATURE_PAL
8170
+ {"/d", &dml, COBOOL, FALSE},
8171
+ #endif
8169
8172
};
8170
8173
8171
8174
if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
8172
8175
{
8173
8176
return Status;
8174
8177
}
8175
8178
8179
+ EnableDMLHolder dmlHolder(dml);
8180
+
8176
8181
ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);
8177
8182
ExtOut ("Worker Thread:");
8178
8183
ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
@@ -8215,6 +8220,152 @@ DECLARE_API(ThreadPool)
8215
8220
workRequestPtr = workRequestData.NextWorkRequest;
8216
8221
}
8217
8222
8223
+ if (doWorkItemDump && g_snapshot.Build())
8224
+ {
8225
+ // Display a message if the heap isn't verified.
8226
+ sos::GCHeap gcheap;
8227
+ if (!gcheap.AreGCStructuresValid())
8228
+ {
8229
+ DisplayInvalidStructuresMessage();
8230
+ }
8231
+
8232
+ // Walk every heap item looking for the global queue and local queues.
8233
+ ExtOut("\nQueued work items:\n%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "Queue", "Address", "Work Item");
8234
+ HeapStat stats;
8235
+ for (sos::ObjectIterator itr = gcheap.WalkHeap(); !IsInterrupt() && itr != NULL; ++itr)
8236
+ {
8237
+ if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue")) == 0)
8238
+ {
8239
+ // We found a global queue (there should be only one, given one AppDomain).
8240
+ // Get its workItems ConcurrentQueue<IThreadPoolWorkItem>.
8241
+ int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("workItems"));
8242
+ if (offset > 0)
8243
+ {
8244
+ DWORD_PTR workItemsConcurrentQueuePtr;
8245
+ MOVE(workItemsConcurrentQueuePtr, itr->GetAddress() + offset);
8246
+ if (sos::IsObject(workItemsConcurrentQueuePtr, false))
8247
+ {
8248
+ // We got the ConcurrentQueue. Get its head segment.
8249
+ sos::Object workItemsConcurrentQueue = TO_TADDR(workItemsConcurrentQueuePtr);
8250
+ offset = GetObjFieldOffset(workItemsConcurrentQueue.GetAddress(), workItemsConcurrentQueue.GetMT(), W("_head"));
8251
+ if (offset > 0)
8252
+ {
8253
+ // Now, walk from segment to segment, each of which contains an array of work items.
8254
+ DWORD_PTR segmentPtr;
8255
+ MOVE(segmentPtr, workItemsConcurrentQueue.GetAddress() + offset);
8256
+ while (sos::IsObject(segmentPtr, false))
8257
+ {
8258
+ sos::Object segment = TO_TADDR(segmentPtr);
8259
+
8260
+ // Get the work items array. It's an array of Slot structs, which starts with the T.
8261
+ offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_slots"));
8262
+ if (offset <= 0)
8263
+ {
8264
+ break;
8265
+ }
8266
+
8267
+ DWORD_PTR slotsPtr;
8268
+ MOVE(slotsPtr, segment.GetAddress() + offset);
8269
+ if (!sos::IsObject(slotsPtr, false))
8270
+ {
8271
+ break;
8272
+ }
8273
+
8274
+ // Walk every element in the array, outputting details on non-null work items.
8275
+ DacpObjectData slotsArray;
8276
+ if (slotsArray.Request(g_sos, TO_CDADDR(slotsPtr)) == S_OK && slotsArray.ObjectType == OBJ_ARRAY)
8277
+ {
8278
+ for (int i = 0; i < slotsArray.dwNumComponents; i++)
8279
+ {
8280
+ CLRDATA_ADDRESS workItemPtr;
8281
+ MOVE(workItemPtr, TO_CDADDR(slotsArray.ArrayDataPtr + (i * slotsArray.dwComponentSize))); // the item object reference is at the beginning of the Slot
8282
+ if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
8283
+ {
8284
+ sos::Object workItem = TO_TADDR(workItemPtr);
8285
+ stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
8286
+ DMLOut("%" POINTERSIZE "s %s %S", "[Global]", DMLObject(workItem.GetAddress()), workItem.GetTypeName());
8287
+ if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
8288
+ (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
8289
+ {
8290
+ CLRDATA_ADDRESS delegatePtr;
8291
+ MOVE(delegatePtr, workItem.GetAddress() + offset);
8292
+ CLRDATA_ADDRESS md;
8293
+ if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
8294
+ {
8295
+ NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
8296
+ ExtOut(" => %S", g_mdName);
8297
+ }
8298
+ }
8299
+ ExtOut("\n");
8300
+ }
8301
+ }
8302
+ }
8303
+
8304
+ // Move to the next segment.
8305
+ DacpFieldDescData segmentField;
8306
+ offset = GetObjFieldOffset(segment.GetAddress(), segment.GetMT(), W("_nextSegment"), TRUE, &segmentField);
8307
+ if (offset <= 0)
8308
+ {
8309
+ break;
8310
+ }
8311
+
8312
+ MOVE(segmentPtr, segment.GetAddress() + offset);
8313
+ if (segmentPtr == NULL)
8314
+ {
8315
+ break;
8316
+ }
8317
+ }
8318
+ }
8319
+ }
8320
+ }
8321
+ }
8322
+ else if (_wcscmp(itr->GetTypeName(), W("System.Threading.ThreadPoolWorkQueue+WorkStealingQueue")) == 0)
8323
+ {
8324
+ // We found a local queue. Get its work items array.
8325
+ int offset = GetObjFieldOffset(itr->GetAddress(), itr->GetMT(), W("m_array"));
8326
+ if (offset > 0)
8327
+ {
8328
+ // Walk every element in the array, outputting details on non-null work items.
8329
+ DWORD_PTR workItemArrayPtr;
8330
+ MOVE(workItemArrayPtr, itr->GetAddress() + offset);
8331
+ DacpObjectData workItemArray;
8332
+ if (workItemArray.Request(g_sos, TO_CDADDR(workItemArrayPtr)) == S_OK && workItemArray.ObjectType == OBJ_ARRAY)
8333
+ {
8334
+ for (int i = 0; i < workItemArray.dwNumComponents; i++)
8335
+ {
8336
+ CLRDATA_ADDRESS workItemPtr;
8337
+ MOVE(workItemPtr, TO_CDADDR(workItemArray.ArrayDataPtr + (i * workItemArray.dwComponentSize)));
8338
+ if (workItemPtr != NULL && sos::IsObject(workItemPtr, false))
8339
+ {
8340
+ sos::Object workItem = TO_TADDR(workItemPtr);
8341
+ stats.Add((DWORD_PTR)workItem.GetMT(), (DWORD)workItem.GetSize());
8342
+ DMLOut("%s %s %S", DMLObject(itr->GetAddress()), DMLObject(workItem.GetAddress()), workItem.GetTypeName());
8343
+ if ((offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("_callback"))) > 0 ||
8344
+ (offset = GetObjFieldOffset(workItem.GetAddress(), workItem.GetMT(), W("m_action"))) > 0)
8345
+ {
8346
+ CLRDATA_ADDRESS delegatePtr;
8347
+ MOVE(delegatePtr, workItem.GetAddress() + offset);
8348
+ CLRDATA_ADDRESS md;
8349
+ if (TryGetMethodDescriptorForDelegate(delegatePtr, &md))
8350
+ {
8351
+ NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
8352
+ ExtOut(" => %S", g_mdName);
8353
+ }
8354
+ }
8355
+ ExtOut("\n");
8356
+ }
8357
+ }
8358
+ }
8359
+ }
8360
+ }
8361
+ }
8362
+
8363
+ // Output a summary.
8364
+ stats.Sort();
8365
+ stats.Print();
8366
+ ExtOut("\n");
8367
+ }
8368
+
8218
8369
if (doHCDump)
8219
8370
{
8220
8371
ExtOut ("--------------------------------------\n");
0 commit comments