diff options
author | Stephen Toub <stoub@microsoft.com> | 2018-10-25 13:01:56 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-25 13:01:56 -0700 |
commit | 468eab186f15e6e42a0041042dc37d97330f6697 (patch) | |
tree | 594050ea0db956481a621305e211b18e0194a334 /src/ToolBox | |
parent | 6dd4270daf3ee49a08adedaf71a087de32c810a2 (diff) | |
download | coreclr-468eab186f15e6e42a0041042dc37d97330f6697.tar.gz coreclr-468eab186f15e6e42a0041042dc37d97330f6697.tar.bz2 coreclr-468eab186f15e6e42a0041042dc37d97330f6697.zip |
Add DumpDelegate sos command (#20591)
* Add DumpDelegate sos command
Finding out what method a delegate points to today with SOS is tedious. This PR adds a DumpDelegate command that simplifies it: give it the delegate address, and it outputs a line for each delegate that makes up the input delegate, where each line includes the target object, the method descriptor, and a friendly name.
* Address PR feedback
And fix some warnings that were showing up in CI but not locally.
Diffstat (limited to 'src/ToolBox')
-rw-r--r-- | src/ToolBox/SOS/Strike/apollososdocs.txt | 34 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/sos.def | 2 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/sos_unixexports.src | 1 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/sosdocs.txt | 30 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/sosdocsunix.txt | 26 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/strike.cpp | 154 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/util.cpp | 56 | ||||
-rw-r--r-- | src/ToolBox/SOS/Strike/util.h | 2 | ||||
-rw-r--r-- | src/ToolBox/SOS/lldbplugin/soscommand.cpp | 1 |
9 files changed, 259 insertions, 47 deletions
diff --git a/src/ToolBox/SOS/Strike/apollososdocs.txt b/src/ToolBox/SOS/Strike/apollososdocs.txt index 6af5f3255e..5b3c352538 100644 --- a/src/ToolBox/SOS/Strike/apollososdocs.txt +++ b/src/ToolBox/SOS/Strike/apollososdocs.txt @@ -26,16 +26,17 @@ Object Inspection Examining code and stacks DumpObj (do) Threads DumpArray (da) ThreadState DumpAsync IP2MD -DumpStackObjects (dso) U -DumpHeap DumpStack -DumpVC EEStack -GCRoot CLRStack -ObjSize GCInfo -FinalizeQueue EHInfo -PrintException (pe) BPMD -TraverseHeap COMState -Watch StopOnCatch - SuppressJitOptimization +DumpDelegate U +DumpStackObjects (dso) DumpStack +DumpHeap EEStack +DumpVC CLRStack +GCRoot GCInfo +ObjSize EHInfo +FinalizeQueue BPMD +PrintException (pe) COMState +TraverseHeap StopOnCatch +Watch SuppressJitOptimization + Examining CLR data structures Diagnostic Utilities ----------------------------- ----------------------------- @@ -419,6 +420,19 @@ interested in objects with invalid fields. 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] diff --git a/src/ToolBox/SOS/Strike/sos.def b/src/ToolBox/SOS/Strike/sos.def index 9ef5d2e7ed..0c47279d75 100644 --- a/src/ToolBox/SOS/Strike/sos.def +++ b/src/ToolBox/SOS/Strike/sos.def @@ -19,6 +19,8 @@ EXPORTS dumpasync=DumpAsync DumpClass dumpclass=DumpClass + DumpDelegate + dumpdelegate=DumpDelegate DumpDomain dumpdomain=DumpDomain #ifdef TRACE_GC diff --git a/src/ToolBox/SOS/Strike/sos_unixexports.src b/src/ToolBox/SOS/Strike/sos_unixexports.src index 463c336976..be8ad961e6 100644 --- a/src/ToolBox/SOS/Strike/sos_unixexports.src +++ b/src/ToolBox/SOS/Strike/sos_unixexports.src @@ -9,6 +9,7 @@ DumpArray DumpAssembly DumpAsync DumpClass +DumpDelegate DumpDomain DumpGCData DumpHeap diff --git a/src/ToolBox/SOS/Strike/sosdocs.txt b/src/ToolBox/SOS/Strike/sosdocs.txt index 93c50ffc64..8b778657b3 100644 --- a/src/ToolBox/SOS/Strike/sosdocs.txt +++ b/src/ToolBox/SOS/Strike/sosdocs.txt @@ -26,14 +26,15 @@ Object Inspection Examining code and stacks DumpObj (do) Threads DumpArray (da) ThreadState DumpAsync IP2MD -DumpStackObjects (dso) U -DumpHeap DumpStack -DumpVC EEStack -GCRoot CLRStack -ObjSize GCInfo -FinalizeQueue EHInfo -PrintException (pe) BPMD -TraverseHeap COMState +DumpDelegate U +DumpStackObjects (dso) DumpStack +DumpHeap EEStack +DumpVC CLRStack +GCRoot GCInfo +ObjSize EHInfo +FinalizeQueue BPMD +PrintException (pe) COMState +TraverseHeap Examining CLR data structures Diagnostic Utilities ----------------------------- ----------------------------- @@ -417,6 +418,19 @@ interested in objects with invalid fields. 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] diff --git a/src/ToolBox/SOS/Strike/sosdocsunix.txt b/src/ToolBox/SOS/Strike/sosdocsunix.txt index 1cc92fdbf9..45e4c433e2 100644 --- a/src/ToolBox/SOS/Strike/sosdocsunix.txt +++ b/src/ToolBox/SOS/Strike/sosdocsunix.txt @@ -26,13 +26,14 @@ Object Inspection Examining code and stacks 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 ----------------------------- ----------------------------- @@ -278,6 +279,19 @@ interested in objects with invalid fields. 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] diff --git a/src/ToolBox/SOS/Strike/strike.cpp b/src/ToolBox/SOS/Strike/strike.cpp index 65e2aaf3ba..b15084804a 100644 --- a/src/ToolBox/SOS/Strike/strike.cpp +++ b/src/ToolBox/SOS/Strike/strike.cpp @@ -124,7 +124,6 @@ #include <set> #include <algorithm> -#include <vector> #include "tls.h" @@ -147,6 +146,8 @@ const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3); #endif // !FEATURE_PAL +#include <vector> + BOOL CallStatus; BOOL ControlC = FALSE; @@ -2057,6 +2058,134 @@ DECLARE_API(DumpObj) 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 @@ -10511,27 +10640,6 @@ DECLARE_API(GCHandles) 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 @@ -10849,7 +10957,7 @@ DECLARE_API(StopOnException) { 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", diff --git a/src/ToolBox/SOS/Strike/util.cpp b/src/ToolBox/SOS/Strike/util.cpp index 9e124c4849..27e8665079 100644 --- a/src/ToolBox/SOS/Strike/util.cpp +++ b/src/ToolBox/SOS/Strike/util.cpp @@ -2468,6 +2468,62 @@ BOOL IsStringObject (size_t obj) 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. diff --git a/src/ToolBox/SOS/Strike/util.h b/src/ToolBox/SOS/Strike/util.h index 1a4cd129ae..a519559072 100644 --- a/src/ToolBox/SOS/Strike/util.h +++ b/src/ToolBox/SOS/Strike/util.h @@ -1846,6 +1846,8 @@ BOOL IsMethodTable (DWORD_PTR value); BOOL IsStringObject (size_t obj); BOOL IsObjectArray (DWORD_PTR objPointer); BOOL IsObjectArray (DacpObjectData *pData); +BOOL IsDerivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPCWSTR baseString); +BOOL TryGetMethodDescriptorForDelegate(CLRDATA_ADDRESS delegateAddr, CLRDATA_ADDRESS* pMD); /* Returns a list of all modules in the process. * Params: diff --git a/src/ToolBox/SOS/lldbplugin/soscommand.cpp b/src/ToolBox/SOS/lldbplugin/soscommand.cpp index bf2912249f..0a54f63526 100644 --- a/src/ToolBox/SOS/lldbplugin/soscommand.cpp +++ b/src/ToolBox/SOS/lldbplugin/soscommand.cpp @@ -129,6 +129,7 @@ sosCommandInitialize(lldb::SBDebugger debugger) interpreter.AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method."); interpreter.AddCommand("dumpasync", new sosCommand("DumpAsync"), "Displays info about async state machines on the garbage-collected heap."); interpreter.AddCommand("dumpclass", new sosCommand("DumpClass"), "Displays information about a EE class structure at the specified address."); + interpreter.AddCommand("dumpdelegate", new sosCommand("DumpDelegate"), "Displays information about a delegate."); interpreter.AddCommand("dumpheap", new sosCommand("DumpHeap"), "Displays info about the garbage-collected heap and collection statistics about objects."); interpreter.AddCommand("dumpil", new sosCommand("DumpIL"), "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method."); interpreter.AddCommand("dumplog", new sosCommand("DumpLog"), "Writes the contents of an in-memory stress log to the specified file."); |