DumpObj (dumpobj) Threads (clrthreads)
DumpArray ThreadState
DumpAsync (dumpasync) IP2MD (ip2md)
-DumpStackObjects (dso) u (clru)
-DumpHeap (dumpheap) DumpStack (dumpstack)
-DumpVC EEStack (eestack)
-GCRoot (gcroot) CLRStack (clrstack)
-PrintException (pe) GCInfo
- EHInfo
+DumpDelegate (dumpdelegate) u (clru)
+DumpStackObjects (dso) DumpStack (dumpstack)
+DumpHeap (dumpheap) EEStack (eestack)
+DumpVC CLRStack (clrstack)
+GCRoot (gcroot) GCInfo
+PrintException (pe) EHInfo
bpmd (bpmd)
+
Examining CLR data structures Diagnostic Utilities
----------------------------- -----------------------------
The abbreviation dso can be used for brevity.
\\
+COMMAND: dumpdelegate.
+!DumpDelegate <delegate address>
+
+!DumpDelegate finds and outputs the one or more method descriptors associated with a delegate object.
+
+For example:
+
+ 0:000> !dumpdelegate
+ Target Method Name
+ 000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod()
+ 000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod()
+\\
+
COMMAND: dumpheap.
DumpHeap [-stat]
[-strings]
#include <set>
#include <algorithm>
-#include <vector>
#include "tls.h"
#endif // !FEATURE_PAL
+#include <vector>
+
BOOL CallStatus;
BOOL ControlC = FALSE;
return Status;
}
+/**********************************************************************\
+* Routine Description: *
+* *
+* This function is called to dump the contents of a delegate from a *
+* given address. *
+* *
+\**********************************************************************/
+
+DECLARE_API(DumpDelegate)
+{
+ INIT_API();
+ MINIDUMP_NOT_SUPPORTED();
+
+ try
+ {
+ BOOL dml = FALSE;
+ DWORD_PTR dwAddr = 0;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"/d", &dml, COBOOL, FALSE}
+ };
+ CMDValue arg[] =
+ { // vptr, type
+ {&dwAddr, COHEX}
+ };
+ size_t nArg;
+ if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
+ {
+ return Status;
+ }
+ if (nArg != 1)
+ {
+ ExtOut("Usage: !DumpDelegate <delegate object address>\n");
+ return Status;
+ }
+
+ EnableDMLHolder dmlHolder(dml);
+ CLRDATA_ADDRESS delegateAddr = TO_CDADDR(dwAddr);
+
+ if (!sos::IsObject(delegateAddr))
+ {
+ ExtOut("Invalid object.\n");
+ }
+ else
+ {
+ sos::Object delegateObj = TO_TADDR(delegateAddr);
+ if (!IsDerivedFrom(TO_CDADDR(delegateObj.GetMT()), W("System.Delegate")))
+ {
+ ExtOut("Object of type '%S' is not a delegate.", delegateObj.GetTypeName());
+ }
+ else
+ {
+ ExtOut("Target Method Name\n");
+
+ std::vector<CLRDATA_ADDRESS> delegatesRemaining;
+ delegatesRemaining.push_back(delegateAddr);
+ while (delegatesRemaining.size() > 0)
+ {
+ delegateAddr = delegatesRemaining.back();
+ delegatesRemaining.pop_back();
+ delegateObj = TO_TADDR(delegateAddr);
+
+ int offset;
+ if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_target"))) != 0)
+ {
+ CLRDATA_ADDRESS target;
+ MOVE(target, delegateObj.GetAddress() + offset);
+
+ if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationList"))) != 0)
+ {
+ CLRDATA_ADDRESS invocationList;
+ MOVE(invocationList, delegateObj.GetAddress() + offset);
+
+ if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_invocationCount"))) != 0)
+ {
+ int invocationCount;
+ MOVE(invocationCount, delegateObj.GetAddress() + offset);
+
+ if (invocationList == NULL)
+ {
+ CLRDATA_ADDRESS md;
+ DMLOut("%s ", DMLObject(target));
+ if (TryGetMethodDescriptorForDelegate(delegateAddr, &md))
+ {
+ DMLOut("%s ", DMLMethodDesc(md));
+ NameForMD_s((DWORD_PTR)md, g_mdName, mdNameLen);
+ ExtOut("%S\n", g_mdName);
+ }
+ else
+ {
+ ExtOut("(unknown)\n");
+ }
+ }
+ else if (sos::IsObject(invocationList, false))
+ {
+ DacpObjectData objData;
+ if (objData.Request(g_sos, invocationList) == S_OK &&
+ objData.ObjectType == OBJ_ARRAY &&
+ invocationCount <= objData.dwNumComponents)
+ {
+ for (int i = 0; i < invocationCount; i++)
+ {
+ CLRDATA_ADDRESS elementPtr;
+ MOVE(elementPtr, TO_CDADDR(objData.ArrayDataPtr + (i * objData.dwComponentSize)));
+ if (elementPtr != NULL && sos::IsObject(elementPtr, false))
+ {
+ delegatesRemaining.push_back(elementPtr);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return S_OK;
+ }
+ catch (const sos::Exception &e)
+ {
+ ExtOut("%s\n", e.what());
+ return E_FAIL;
+ }
+}
+
CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
{
// We want to follow back until we get the mt for System.Exception
return Status;
}
-BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
-{
- // We want to follow back until we get the mt for System.Exception
- DacpMethodTableData dmtd;
- CLRDATA_ADDRESS walkMT = mtObj;
- while(walkMT != NULL)
- {
- if (dmtd.Request(g_sos, walkMT) != S_OK)
- {
- break;
- }
- NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);
- if (_wcscmp (baseString, g_mdName) == 0)
- {
- return TRUE;
- }
- walkMT = dmtd.ParentMethodTable;
- }
- return FALSE;
-}
-
// This is an experimental and undocumented SOS API that attempts to step through code
// stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
// to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
{
NameForMT_s (taMT, g_mdName, mdNameLen);
if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
- (fDerived && derivedFrom(taMT, typeNameWide)))
+ (fDerived && IsDerivedFrom(taMT, typeNameWide)))
{
sprintf_s(buffer,_countof (buffer),
"r$t%d=1",
return FALSE;
}
+BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString)
+{
+ DacpMethodTableData dmtd;
+ CLRDATA_ADDRESS walkMT = mtObj;
+ while (walkMT != NULL)
+ {
+ if (dmtd.Request(g_sos, walkMT) != S_OK)
+ {
+ break;
+ }
+
+ NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);
+ if (_wcscmp(baseString, g_mdName) == 0)
+ {
+ return TRUE;
+ }
+
+ walkMT = dmtd.ParentMethodTable;
+ }
+
+ return FALSE;
+}
+
+BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD)
+{
+ if (!sos::IsObject(delegateAddr, false))
+ {
+ return FALSE;
+ }
+
+ sos::Object delegateObj = TO_TADDR(delegateAddr);
+ int offset;
+
+ if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_methodPtrAux"))) != 0)
+ {
+ CLRDATA_ADDRESS methodPtrAux;
+ MOVE(methodPtrAux, delegateObj.GetAddress() + offset);
+ if (methodPtrAux != NULL && g_sos->GetMethodDescPtrFromIP(methodPtrAux, pMD) == S_OK)
+ {
+ return TRUE;
+ }
+ }
+
+ if ((offset = GetObjFieldOffset(delegateObj.GetAddress(), delegateObj.GetMT(), W("_methodPtr"))) != 0)
+ {
+ CLRDATA_ADDRESS methodPtr;
+ MOVE(methodPtr, delegateObj.GetAddress() + offset);
+ if (methodPtr != NULL && g_sos->GetMethodDescPtrFromIP(methodPtr, pMD) == S_OK)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
void DumpStackObjectsOutput(const char *location, DWORD_PTR objAddr, BOOL verifyFields)
{
// rule out pointers that are outside of the gc heap.