Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/SOS/Strike/sosdocs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1065,11 +1065,13 @@ You can use "!DumpHeap -thinlock" to list objects locked in this way.
\\

COMMAND: dumpmt.
!DumpMT [-MD] <MethodTable address>
!DumpMT [-MD] [-all] <MethodTable address>

Examine a MethodTable. Each managed object has a MethodTable pointer at the
start. If you pass the "-MD" flag, you'll also see a list of all the methods
defined on the object.
defined on the object. If you pass the "-all" flag, you'll see class details
(attributes and fields) as well as the method list. The "-all" flag implies
"-MD".
\\

COMMAND: dumpclass.
Expand Down
6 changes: 4 additions & 2 deletions src/SOS/Strike/sosdocsunix.txt
Original file line number Diff line number Diff line change
Expand Up @@ -980,11 +980,13 @@ You can use "dumpheap -thinlock" to list objects locked in this way.
\\

COMMAND: dumpmt.
DumpMT [-MD] <MethodTable address>
DumpMT [-MD] [-all] <MethodTable address>

Examine a MethodTable. Each managed object has a MethodTable pointer at the
start. If you pass the "-MD" flag, you'll also see a list of all the methods
defined on the object.
defined on the object. If you pass the "-all" flag, you'll see class details
(attributes and fields) as well as the method list. The "-all" flag implies
"-MD".
\\

COMMAND: dumpclass.
Expand Down
184 changes: 109 additions & 75 deletions src/SOS/Strike/strike.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,59 @@ DECLARE_API(DumpSigElem)
return Status;
}

/**********************************************************************\
* Routine Description: *
* *
* Helper function to display class details (attributes, vtable *
* slots, and fields). *
* *
\**********************************************************************/
static void DisplayClassDetails(CLRDATA_ADDRESS methodTable, DacpMethodTableData *pMTData)
{
ExtOut("Class Attributes: %08x ", pMTData->dwAttrClass);
if (IsTdInterface(pMTData->dwAttrClass))
ExtOut("Interface ");
if (IsTdAbstract(pMTData->dwAttrClass))
ExtOut("Abstract ");
if (IsTdImport(pMTData->dwAttrClass))
ExtOut("ComImport ");
if (IsTdSealed(pMTData->dwAttrClass))
ExtOut("Sealed ");
ExtOut("\n");

if (pMTData->wNumVirtuals != 0)
{
ExtOut("Vtable Slots: %d\n", pMTData->wNumVirtuals);
}
if (pMTData->wNumVtableSlots != 0)
{
ExtOut("Total Method Slots: %d\n", pMTData->wNumVtableSlots);
}

DacpMethodTableFieldData vMethodTableFields;
if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
{
ExtOut("NumInstanceFields: %d\n", vMethodTableFields.wNumInstanceFields);
ExtOut("NumStaticFields: %d\n", vMethodTableFields.wNumStaticFields);

if (vMethodTableFields.wNumThreadStaticFields != 0)
{
ExtOut("NumThreadStaticFields: %d\n", vMethodTableFields.wNumThreadStaticFields);
}

if (vMethodTableFields.wContextStaticsSize)
{
ExtOut("ContextStaticOffset: 0x%x\n", vMethodTableFields.wContextStaticOffset);
ExtOut("ContextStaticsSize: %d\n", vMethodTableFields.wContextStaticsSize);
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
}

if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
{
DisplayFields(methodTable, pMTData, &vMethodTableFields, (TADDR)0, TRUE, FALSE);
}
}
}

/**********************************************************************\
* Routine Description: *
* *
Expand Down Expand Up @@ -1096,57 +1149,16 @@ DECLARE_API(DumpClass)
}
else
{
DMLOut("Parent MethodTable: %s\n", DMLMethodTable(mtdata.ParentMethodTable));
DMLOut("Parent: %s\n", DMLMethodTable(mtdata.ParentMethodTable));
}
DMLOut("Module: %s\n", DMLModule(mtdata.Module));
DMLOut("Method Table: %s\n", DMLMethodTable(methodTable));
if (preferMT)
{
DMLOut("Canonical MethodTable: %s\n", DMLClass(mtdata.Class));
}
if (mtdata.wNumVirtuals != 0)
if (preferMT && methodTable != mtdata.Class)
{
ExtOut("Vtable Slots: %x\n", mtdata.wNumVirtuals);
DMLOut("Canonical: %s\n", DMLMethodTable(mtdata.Class));
}
if (mtdata.wNumVtableSlots != 0)
{
ExtOut("Total Method Slots: %x\n", mtdata.wNumVtableSlots);
}
ExtOut("Class Attributes: %x ", mtdata.dwAttrClass);

if (IsTdInterface(mtdata.dwAttrClass))
ExtOut("Interface, ");
if (IsTdAbstract(mtdata.dwAttrClass))
ExtOut("Abstract, ");
if (IsTdImport(mtdata.dwAttrClass))
ExtOut("ComImport, ");

ExtOut("\n");

DacpMethodTableFieldData vMethodTableFields;
if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
{
ExtOut("NumInstanceFields: %x\n", vMethodTableFields.wNumInstanceFields);
ExtOut("NumStaticFields: %x\n", vMethodTableFields.wNumStaticFields);

if (vMethodTableFields.wNumThreadStaticFields != 0)
{
ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
}


if (vMethodTableFields.wContextStaticsSize)
{
ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
ExtOut("ContextStaticsSize: %x\n", vMethodTableFields.wContextStaticsSize);
}


if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
{
DisplayFields(methodTable, &mtdata, &vMethodTableFields, (TADDR)0, TRUE, FALSE);
}
}
DisplayClassDetails(methodTable, &mtdata);

return Status;
}
Expand All @@ -1168,11 +1180,13 @@ DECLARE_API(DumpMT)
MINIDUMP_NOT_SUPPORTED();

BOOL bDumpMDTable = FALSE;
BOOL bDumpAll = FALSE;
BOOL dml = FALSE;

CMDOption option[] =
{ // name, vptr, type, hasValue
{"-MD", &bDumpMDTable, COBOOL, FALSE},
{"-all", &bDumpAll, COBOOL, FALSE},
{"/d", &dml, COBOOL, FALSE}
};
CMDValue arg[] =
Expand All @@ -1188,6 +1202,9 @@ DECLARE_API(DumpMT)
EnableDMLHolder dmlHolder(dml);
TableOutput table(2, 20, AlignLeft, false);

if (bDumpAll)
bDumpMDTable = TRUE;

if (nArg == 0)
{
Print("Missing MethodTable address\n");
Expand Down Expand Up @@ -1215,13 +1232,25 @@ DECLARE_API(DumpMT)
DacpMethodTableCollectibleData vMethTableCollectible;
vMethTableCollectible.Request(g_sos, TO_CDADDR(dwStartAddr));

BOOL preferCanonMT = FALSE;
if (SUCCEEDED(PreferCanonMTOverEEClass(vMethTable.Class, &preferCanonMT)) && preferCanonMT)
// Check if runtime returns canonical MT instead of EEClass (.NET 9+)
BOOL runtimePrefersCanonMT = FALSE;
CLRDATA_ADDRESS canonicalMT = 0;
Status = PreferCanonMTOverEEClass(vMethTable.Class, &runtimePrefersCanonMT, &canonicalMT);

table.WriteRow("Parent:", DMLMethodTable(vMethTable.ParentMethodTable));

if (SUCCEEDED(Status) && runtimePrefersCanonMT)
{
table.WriteRow("Canonical MethodTable:", EEClassPtr(vMethTable.Class));
// .NET 9+: vMethTable.Class contains canonical MT, not EEClass
// Only show "Canonical" if it differs from the current MT
if (canonicalMT != 0 && canonicalMT != TO_CDADDR(dwStartAddr))
{
table.WriteRow("Canonical:", DMLMethodTable(canonicalMT));
}
}
else
{
// Legacy: vMethTable.Class contains EEClass
table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
}

Expand Down Expand Up @@ -1263,9 +1292,9 @@ DECLARE_API(DumpMT)
}

table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
table.WriteRow("DynamicStatics:", vMethTable.bIsDynamic ? "true" : "false");
table.WriteRow("ContainsPointers:", vMethTable.bContainsPointers ? "true" : "false");
if (vMethTable.ComponentSize != 0)
table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
table.WriteRow("Has GC Pointers:", vMethTable.bContainsPointers ? "true" : "false");
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
table.WriteRow("Number of Methods:", Decimal(vMethTable.wNumMethods));

table.SetColWidth(0, 29);
Expand Down Expand Up @@ -1363,6 +1392,14 @@ DECLARE_API(DumpMT)
}
}
}
// When -all is specified, include class details (similar to DumpClass output)
if (bDumpAll)
{
Print("--------------------------------------\n");
Print("Additional Details\n");

DisplayClassDetails(TO_CDADDR(dwStartAddr), &vMethTable);
}
return Status;
}

Expand All @@ -1381,13 +1418,24 @@ HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)

ExtOut("Name: %S\n", g_mdName);
DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
BOOL preferCanonMT = FALSE;
if (SUCCEEDED(PreferCanonMTOverEEClass(TO_CDADDR(taMT), &preferCanonMT)) && preferCanonMT)

// Check if runtime returns canonical MT instead of EEClass (.NET 9+)
BOOL runtimePrefersCanonMT = FALSE;
CLRDATA_ADDRESS canonicalMT = 0;
Status = PreferCanonMTOverEEClass(mtabledata.Class, &runtimePrefersCanonMT, &canonicalMT);

if (SUCCEEDED(Status) && runtimePrefersCanonMT)
{
DMLOut("Canonical MethodTable: %s\n", DMLClass(mtabledata.Class));
// .NET 9+: mtabledata.Class contains canonical MT, not EEClass
// Only show "Canonical" if it differs from the current MT
if (canonicalMT != 0 && canonicalMT != TO_CDADDR(taMT))
{
DMLOut("Canonical: %s\n", DMLMethodTable(canonicalMT));
}
}
else
{
// Legacy: mtabledata.Class contains EEClass
DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
}
ExtOut("Size: %d(0x%x) bytes\n", size, size);
Expand Down Expand Up @@ -1478,23 +1526,10 @@ HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
ExtOut("Name: %S\n", obj.GetTypeName());
DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));


DacpMethodTableData mtabledata;
if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
{
BOOL preferCanonMT = FALSE;
if (SUCCEEDED(PreferCanonMTOverEEClass(mtabledata.Class, &preferCanonMT)) && preferCanonMT)
{
DMLOut("Canonical MethodTable: %s\n", DMLClass(mtabledata.Class));
}
else
{
DMLOut("EEClass: %s\n", DMLClass(mtabledata.Class));
}
}
else
if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) != S_OK)
{
ExtOut("Invalid EEClass address\n");
ExtOut("Invalid MethodTable address\n");
return Status;
}

Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Expand Down Expand Up @@ -1557,9 +1592,12 @@ HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
CLRDATA_ADDRESS objAddr = TO_CDADDR(taObj);
BOOL isTrackedType;
BOOL hasTaggedMemory;
if (SUCCEEDED(sos11->IsTrackedType(objAddr, &isTrackedType, &hasTaggedMemory)))
if (SUCCEEDED(sos11->IsTrackedType(objAddr, &isTrackedType, &hasTaggedMemory))
&& (isTrackedType || hasTaggedMemory))
{
ExtOut("Tracked Type: %s\n", isTrackedType ? "true" : "false");
if (isTrackedType)
ExtOut("Tracked Type: true\n");

if (hasTaggedMemory)
{
CLRDATA_ADDRESS taggedMemory = (TADDR)0;
Expand Down Expand Up @@ -1639,10 +1677,6 @@ HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
StringObjectContent(taObj);
ExtOut("\n");
}
else if (objData.ObjectType == OBJ_OBJECT)
{
ExtOut("Object\n");
}

if (bPrintFields)
{
Expand Down
20 changes: 12 additions & 8 deletions src/SOS/Strike/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,10 +606,14 @@ HRESULT GetStaticFieldPTR(DWORD_PTR* pOutPtr, DacpDomainLocalModuleData* pDLMD,
}
}

dwTmp = (DWORD_PTR)pBaseAddress + pFDD->dwOffset;

*pOutPtr = 0;

// Statics for this type may not be allocated yet, this is okay.
// See dynamic statics for more information.
if (pBaseAddress == 0)
return S_OK;

dwTmp = (DWORD_PTR)pBaseAddress + pFDD->dwOffset;
if (pSOS14)
{
MethodTableInitializationFlags initFlags;
Expand Down Expand Up @@ -1032,7 +1036,7 @@ void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodT
if (bFirst)
{
ExtOutIndent();
ExtOut("%" POINTERSIZE "s %8s %8s %20s %2s %8s %" POINTERSIZE "s %s\n",
ExtOut("%" POINTERSIZE "s %8s %8s %20s %4s %8s %" POINTERSIZE "s %s\n",
"MT", "Field", "Offset", "Type", "VT", "Attr", "Value", "Name");
numInstanceFields = 0;
}
Expand Down Expand Up @@ -1094,7 +1098,7 @@ void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodT
}
}

DMLOut("%s %8x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
DMLOut("%s %08x %8x ", DMLMethodTable(vFieldDesc.MTOfType),
TokenFromRid(vFieldDesc.mb, mdtFieldDef),
offset);

Expand Down Expand Up @@ -1123,7 +1127,7 @@ void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodT
}
}

ExtOut("%2s ", (IsElementValueType(vFieldDesc.Type)) ? "1" : "0");
ExtOut("%4s ", (IsElementValueType(vFieldDesc.Type)) ? "Yes" : "No");

if (vFieldDesc.bIsStatic && (vFieldDesc.bIsThreadLocal || vFieldDesc.bIsContextLocal))
{
Expand Down Expand Up @@ -1229,7 +1233,7 @@ void DisplayFields(CLRDATA_ADDRESS cdaMT, DacpMethodTableData *pMTD, DacpMethodT
}
else
{
ExtOut(" %8s", " ");
ExtOut("%" POINTERSIZE "s", " ");
}


Expand Down Expand Up @@ -5524,7 +5528,7 @@ WString DmlEscape(const WString &input)
const WCHAR *str = input.c_str();
size_t len = input.length();
WString result;

for (size_t i = 0; i < len; i++)
{
// Ampersand must be escaped FIRST to avoid double-escaping
Expand All @@ -5548,7 +5552,7 @@ WString DmlEscape(const WString &input)
result += temp;
}
}

return result;
}

Expand Down
Loading