.net9+: prefer the canonical method table over EEClass (#4758)
authorAleksey Kliger (λgeek) <akliger@gmail.com>
Thu, 1 Aug 2024 17:22:16 +0000 (13:22 -0400)
committerGitHub <noreply@github.com>
Thu, 1 Aug 2024 17:22:16 +0000 (10:22 -0700)
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h

index 9cc7c472f003bbf493987aeb77526af73ef7c0f4..282182b23ad4b75d5df40a13e9533ea8a9852856 100644 (file)
@@ -1048,7 +1048,8 @@ DECLARE_API(DumpClass)
     EnableDMLHolder dmlHolder(dml);
 
     CLRDATA_ADDRESS methodTable;
-    if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
+    BOOL preferMT = FALSE;
+    if (!SUCCEEDED(Status = PreferCanonMTOverEEClass(TO_CDADDR(dwStartAddr), &preferMT, &methodTable)))
     {
         ExtOut("Invalid EEClass address\n");
         return Status;
@@ -1081,9 +1082,20 @@ DECLARE_API(DumpClass)
         ParentEEClass = mtdataparent.Class;
     }
 
-    DMLOut("Parent Class:    %s\n", DMLClass(ParentEEClass));
+    if (!preferMT)
+    {
+        DMLOut("Parent Class:    %s\n", DMLClass(ParentEEClass));
+    }
+    else
+    {
+        DMLOut("Parent MethodTable: %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));
+    }
     ExtOut("Vtable Slots:    %x\n", mtdata.wNumVirtuals);
     ExtOut("Total Method Slots:  %x\n", mtdata.wNumVtableSlots);
     ExtOut("Class Attributes:    %x  ", mtdata.dwAttrClass);
@@ -1189,7 +1201,15 @@ DECLARE_API(DumpMT)
     DacpMethodTableCollectibleData vMethTableCollectible;
     vMethTableCollectible.Request(g_sos, TO_CDADDR(dwStartAddr));
 
-    table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
+    BOOL preferCanonMT = FALSE;
+    if (SUCCEEDED(PreferCanonMTOverEEClass(vMethTable.Class, &preferCanonMT)) && preferCanonMT)
+    {
+        table.WriteRow("Canonical MethodTable:", EEClassPtr(vMethTable.Class));
+    }
+    else
+    {
+        table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
+    }
 
     table.WriteRow("Module:", ModulePtr(vMethTable.Module));
 
@@ -1347,7 +1367,15 @@ HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
 
     ExtOut("Name:        %S\n", g_mdName);
     DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
-    DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
+    BOOL preferCanonMT = FALSE;
+    if (SUCCEEDED(PreferCanonMTOverEEClass(TO_CDADDR(taMT), &preferCanonMT)) && preferCanonMT)
+    {
+        DMLOut("Canonical MethodTable: %s\n", DMLClass(mtabledata.Class));
+    }
+    else
+    {
+        DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
+    }
     ExtOut("Size:        %d(0x%x) bytes\n", size, size);
 
     FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
@@ -1440,7 +1468,15 @@ HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
     DacpMethodTableData mtabledata;
     if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
     {
-        DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
+        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
     {
@@ -5791,7 +5827,7 @@ BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
         return FALSE;
     }
 
-    // The new DAC based interface doesn't exists so ask the debugger for the last exception information. 
+    // The new DAC based interface doesn't exists so ask the debugger for the last exception information.
 
 #ifdef HOST_WINDOWS
     ULONG Type, ProcessId, ThreadId;
index c80a47079b6c54806ce0151b616e21fad713532a..32be745687903bf50065b95d4275410307cd1df7 100644 (file)
@@ -5865,3 +5865,30 @@ HRESULT GetMetadataMemory(CLRDATA_ADDRESS address, ULONG32 bufferSize, BYTE* buf
 }
 
 #endif // FEATURE_PAL
+
+/**********************************************************************\
+* Routine Description:                                                 *
+*                                                                      *
+*    Since .NET 9+ the runtime does not expose EEClass, but instead    *
+*    returns a pointer to the canonical MethodTable in                 *
+*    DacpMethodTableData:Class.                                        *
+*    Detect that situation by calling GetMethodTableForEEClass and     *
+*    comparing the result to the EEClass itself.                       *
+*                                                                      *
+\**********************************************************************/
+
+HRESULT PreferCanonMTOverEEClass(CLRDATA_ADDRESS eeClassPtr, BOOL *preferCanonMT, CLRDATA_ADDRESS *outCanonMT)
+{
+    HRESULT Status;
+    CLRDATA_ADDRESS canonMT = 0;
+    if (!SUCCEEDED(Status = g_sos->GetMethodTableForEEClass(eeClassPtr, &canonMT)))
+    {
+        return Status;
+    }
+    *preferCanonMT = (eeClassPtr == canonMT);
+    if (outCanonMT)
+    {
+        *outCanonMT = canonMT;
+    }
+    return S_OK;
+}
index 3c2bfb8bb0df0893697d9b47fa450030a743232e..f63cf790d087f2d1b23ac8d735fe85f862220403 100644 (file)
@@ -2142,6 +2142,8 @@ WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE
 HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
 WString GetFrameFromAddress(TADDR frameAddr, IXCLRDataStackWalk *pStackwalk = NULL, BOOL bAssemblyName = FALSE);
 
+HRESULT PreferCanonMTOverEEClass(CLRDATA_ADDRESS eeClassPtr, BOOL *preferCanonMT, CLRDATA_ADDRESS *outCanonMT = NULL);
+
 /* This cache is used to read data from the target process if the reads are known
  * to be sequential.
  */