summaryrefslogtreecommitdiff
path: root/src/ToolBox
diff options
context:
space:
mode:
authorStephen Toub <stoub@microsoft.com>2018-10-25 13:01:56 -0700
committerGitHub <noreply@github.com>2018-10-25 13:01:56 -0700
commit468eab186f15e6e42a0041042dc37d97330f6697 (patch)
tree594050ea0db956481a621305e211b18e0194a334 /src/ToolBox
parent6dd4270daf3ee49a08adedaf71a087de32c810a2 (diff)
downloadcoreclr-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.txt34
-rw-r--r--src/ToolBox/SOS/Strike/sos.def2
-rw-r--r--src/ToolBox/SOS/Strike/sos_unixexports.src1
-rw-r--r--src/ToolBox/SOS/Strike/sosdocs.txt30
-rw-r--r--src/ToolBox/SOS/Strike/sosdocsunix.txt26
-rw-r--r--src/ToolBox/SOS/Strike/strike.cpp154
-rw-r--r--src/ToolBox/SOS/Strike/util.cpp56
-rw-r--r--src/ToolBox/SOS/Strike/util.h2
-rw-r--r--src/ToolBox/SOS/lldbplugin/soscommand.cpp1
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.");