Repository: dotnet/runtime
Pull request #115572: Enumerate all NativeCodeVersions in Heap dumps
Author: login: max-charlamb
Labels: name: area-Diagnostics-coreclr
Created: 2025-05-14T18:14:28Z | Merged: 2025-05-16T17:05:52Z

Found this issue when testing the cDAC.

The current DAC EnumMemoryRegion API, used to collect memory in Heap dumps, does not accurately record memory of functions on the stack. 
The previous behavior did a stack walk. At each frame, it would check if there is a managed function (`MethodDesc`). If there is, then it would record data about `methodDesc->GetNativeCode()`. However, a MethodDesc can have many native code versions, several of which can be on the stack at the same time. In the current implementation only one of these would ever be captured.

This PR modifies `MethodDesc::EnumMemoryRegions` to iterate all `NativeCodeVersions` and save DebugInfo/UnwindInfo for each of them.


Diff:
diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp
index 95d401caa0daa9..0f5d418d648ba8 100644
--- a/src/coreclr/debug/daccess/enummem.cpp
+++ b/src/coreclr/debug/daccess/enummem.cpp
@@ -591,8 +591,6 @@ HRESULT ClrDataAccess::DumpManagedExcepObject(CLRDataEnumMemoryFlags flags, OBJE
             // Pulls in data to translate from token to MethodDesc
             FindLoadedMethodRefOrDef(pMD->GetMethodTable()->GetModule(), pMD->GetMemberDef());
 
-            // Pulls in sequence points.
-            DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMD);
             PCODE addr = pMD->GetNativeCode();
             if (addr != (PCODE)NULL)
             {
@@ -970,9 +968,6 @@ HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags,
                             // back to source lines for functions on stacks is very useful and we don't
                             // want to allow the function to fail for all targets.
 
-                            // Pulls in sequence points and local variable info
-                            DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMethodDesc);
-
 #if defined(FEATURE_EH_FUNCLETS) && defined(USE_GC_INFO_DECODER)
 
                             if (addr != (PCODE)NULL)
diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp
index 35d3f0165ab84c..06ab31bc0e4ffc 100644
--- a/src/coreclr/vm/codeman.cpp
+++ b/src/coreclr/vm/codeman.cpp
@@ -4148,6 +4148,14 @@ void CodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJitManager* pJ
 
     this->pRealCodeHeader.EnumMem();
 
+#ifdef FEATURE_EH_FUNCLETS
+    // UnwindInfos are stored in an array immediately following the RealCodeHeader structure in memory.
+    if (this->pRealCodeHeader->nUnwindInfos)
+    {
+        DacEnumMemoryRegion(PTR_TO_MEMBER_TADDR(RealCodeHeader, pRealCodeHeader, unwindInfos), this->pRealCodeHeader->nUnwindInfos * sizeof(T_RUNTIME_FUNCTION));
+    }
+#endif // FEATURE_EH_FUNCLETS
+
 #ifdef FEATURE_ON_STACK_REPLACEMENT
     BOOL hasFlagByte = TRUE;
 #else
@@ -4164,7 +4172,7 @@ void CodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJitManager* pJ
 // Enumerate for minidumps.
 //-----------------------------------------------------------------------------
 template<typename TCodeHeader>
-void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
 {
     CONTRACTL
     {
@@ -4175,15 +4183,14 @@ void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemo
     CONTRACTL_END;
 
     DebugInfoRequest request;
-    PCODE addrCode = pMD->GetNativeCode();
-    request.InitFromStartingAddr(pMD, addrCode);
+    request.InitFromStartingAddr(pCodeInfo->GetMethodDesc(), pCodeInfo->GetStartAddress());
 
     TCodeHeader * pHeader = GetCodeHeaderFromDebugInfoRequest<TCodeHeader>(request);
 
     pHeader->EnumMemoryRegions(flags, NULL);
 }
 
-void EEJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void EEJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
 {
     CONTRACTL
     {
@@ -4193,7 +4200,7 @@ void EEJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags fl
     }
     CONTRACTL_END;
 
-    EnumMemoryRegionsForMethodDebugInfoWorker<CodeHeader>(flags, pMD);
+    EnumMemoryRegionsForMethodDebugInfoWorker<CodeHeader>(flags, pCodeInfo);
 }
 
 #ifdef FEATURE_INTERPRETER
@@ -4217,7 +4224,7 @@ void InterpreterCodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJit
     }
 }
 
-void InterpreterJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void InterpreterJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
 {
     CONTRACTL
     {
@@ -4227,7 +4234,7 @@ void InterpreterJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemor
     }
     CONTRACTL_END;
 
-    EnumMemoryRegionsForMethodDebugInfoWorker<InterpreterCodeHeader>(flags, pMD);
+    EnumMemoryRegionsForMethodDebugInfoWorker<InterpreterCodeHeader>(flags, pCodeInfo);
 }
 #endif // FEATURE_INTERPRETER
 
@@ -6314,16 +6321,15 @@ BOOL ReadyToRunJitManager::GetRichDebugInfo(
 //
 // Need to write out debug info
 //
-void ReadyToRunJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void ReadyToRunJitManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
 {
     SUPPORTS_DAC;
 
-    EECodeInfo codeInfo(pMD->GetNativeCode());
-    if (!codeInfo.IsValid())
+    if (!pCodeInfo->IsValid())
         return;
 
-    ReadyToRunInfo * pReadyToRunInfo = JitTokenToReadyToRunInfo(codeInfo.GetMethodToken());
-    PTR_RUNTIME_FUNCTION pRuntimeFunction = JitTokenToRuntimeFunction(codeInfo.GetMethodToken());
+    ReadyToRunInfo * pReadyToRunInfo = JitTokenToReadyToRunInfo(pCodeInfo->GetMethodToken());
+    PTR_RUNTIME_FUNCTION pRuntimeFunction = JitTokenToRuntimeFunction(pCodeInfo->GetMethodToken());
 
     PTR_BYTE pDebugInfo = pReadyToRunInfo->GetDebugInfo(pRuntimeFunction);
     if (pDebugInfo == NULL)
diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h
index 9e2eabdeecc09a..edad07c4341f8b 100644
--- a/src/coreclr/vm/codeman.h
+++ b/src/coreclr/vm/codeman.h
@@ -1781,7 +1781,7 @@ class IJitManager
     // DAC builds is compatible with the non-DAC one so that DAC virtual dispatch will work correctly.
 #if defined(DACCESS_COMPILE)
     virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
-    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD) = 0;
+    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo) = 0;
 #if defined(FEATURE_EH_FUNCLETS)
     // Enumerate the memory necessary to retrieve the unwind info for a specific method
     virtual void EnumMemoryRegionsForMethodUnwindInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo) = 0;
@@ -1924,7 +1924,7 @@ class EECodeGenManager : public IJitManager
     virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
 protected:
     template<typename TCodeHeader>
-    void EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, MethodDesc * pMD);
+    void EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
 #endif // !DACCESS_COMPILE
 
 private:
@@ -2085,7 +2085,7 @@ class EEJitManager final : public EECodeGenManager
     GCInfoToken         GetGCInfoToken(const METHODTOKEN& MethodToken);
 
 #ifdef DACCESS_COMPILE
-    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD);
+    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
 #endif // DACCESS_COMPILE
 
 #if !defined DACCESS_COMPILE
@@ -2671,7 +2671,7 @@ class ReadyToRunJitManager final: public IJitManager
 
 #if defined(DACCESS_COMPILE)
     virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
-    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD);
+    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
 #if defined(FEATURE_EH_FUNCLETS)
     // Enumerate the memory necessary to retrieve the unwind info for a specific method
     virtual void EnumMemoryRegionsForMethodUnwindInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
@@ -2791,7 +2791,7 @@ class InterpreterJitManager final : public EECodeGenManager
 
 #if defined(DACCESS_COMPILE)
 
-    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD);
+    virtual void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
 
 #if defined(FEATURE_EH_FUNCLETS)
     virtual void EnumMemoryRegionsForMethodUnwindInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
@@ -2941,7 +2941,7 @@ class EECodeInfo
         {
             return DecodeGCHdrInfoHelper(infoPtr);
         }
-    
+
         *infoPtr = &m_hdrInfoBody;
         return m_hdrInfoTable;
     }
diff --git a/src/coreclr/vm/debuginfostore.cpp b/src/coreclr/vm/debuginfostore.cpp
index 789693f33b68ab..e3ef94a196066b 100644
--- a/src/coreclr/vm/debuginfostore.cpp
+++ b/src/coreclr/vm/debuginfostore.cpp
@@ -1025,7 +1025,7 @@ BOOL DebugInfoManager::GetRichDebugInfo(
 }
 
 #ifdef DACCESS_COMPILE
-void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
 {
     CONTRACTL
     {
@@ -1035,18 +1035,23 @@ void DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlag
     }
     CONTRACTL_END;
 
-    PCODE addrCode = pMD->GetNativeCode();
+    if (!pCodeInfo->IsValid())
+    {
+        return;
+    }
+
+    PCODE addrCode = pCodeInfo->GetStartAddress();
     if (addrCode == (PCODE)NULL)
     {
         return;
     }
 
-    IJitManager* pJitMan = ExecutionManager::FindJitMan(addrCode);
+    IJitManager* pJitMan = pCodeInfo->GetJitManager();
     if (pJitMan == NULL)
     {
         return; // no info available.
     }
 
-    pJitMan->EnumMemoryRegionsForMethodDebugInfo(flags, pMD);
+    pJitMan->EnumMemoryRegionsForMethodDebugInfo(flags, pCodeInfo);
 }
 #endif
diff --git a/src/coreclr/vm/debuginfostore.h b/src/coreclr/vm/debuginfostore.h
index 1df939432ce6d7..699940486e2130 100644
--- a/src/coreclr/vm/debuginfostore.h
+++ b/src/coreclr/vm/debuginfostore.h
@@ -154,7 +154,7 @@ class DebugInfoManager
         OUT ULONG32*                           pNumRichMappings);
 
 #ifdef DACCESS_COMPILE
-    static void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, MethodDesc * pMD);
+    static void EnumMemoryRegionsForMethodDebugInfo(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo);
 #endif
 };
 
diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp
index b4b6c5185a0941..5d6667a4cf1a01 100644
--- a/src/coreclr/vm/method.cpp
+++ b/src/coreclr/vm/method.cpp
@@ -3673,7 +3673,24 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager::LockHolder codeVersioningLockHolder;
+
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
+            EECodeInfo codeInfo(addrCode);
+            DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, &codeInfo);
+        }
+    }
+#else
+    PCODE entryPoint = GetNativeCode();
+    EECodeInfo codeInfo(entryPoint);
+    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, &codeInfo);
+#endif // FEATURE_CODE_VERSIONING
 
     if (!IsNoMetadata() ||IsILStub())
     {
diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs
index 925d8d75d8ba0c..617c02af49b0e4 100644
--- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs
+++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/Unwinder.cs
@@ -146,11 +146,16 @@ private static unsafe void GetStackWalkInfo(ulong controlPC, void* pUnwindInfoBa
         {
             if (eman.GetCodeBlockHandle(controlPC) is CodeBlockHandle cbh)
             {
-                TargetPointer methodDesc = eman.GetMethodDesc(cbh);
-                TargetPointer unwindInfoBase = eman.GetUnwindInfoBaseAddress(cbh);
-                TargetPointer unwindInfo = eman.GetUnwindInfo(cbh, controlPC);
-                if ((nuint)pUnwindInfoBase != 0) *(nuint*)pUnwindInfoBase = (nuint)unwindInfoBase.Value;
-                if ((nuint)pFuncEntry != 0) *(nuint*)pFuncEntry = (nuint)unwindInfo.Value;
+                if ((nuint)pUnwindInfoBase != 0)
+                {
+                    TargetPointer unwindInfoBase = eman.GetUnwindInfoBaseAddress(cbh);
+                    *(nuint*)pUnwindInfoBase = (nuint)unwindInfoBase.Value;
+                }
+                if ((nuint)pFuncEntry != 0)
+                {
+                    TargetPointer unwindInfo = eman.GetUnwindInfo(cbh, controlPC);
+                    *(nuint*)pFuncEntry = (nuint)unwindInfo.Value;
+                }
             }
         }
         catch (System.Exception ex)


Reviews:
user: login: max-charlamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T18:16:58Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:07:10Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:08:45Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:09:02Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:14:20Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:18:37Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:22:21Z
user: login: max-charlamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:31:26Z
user: login: max-charlamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:33:25Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:34:27Z
user: login: max-charlamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:35:54Z
user: login: kg
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T19:56:33Z
user: login: davidwrighton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T20:47:21Z
user: login: max-charlamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-15T17:07:07Z
user: login: davidwrighton
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T16:50:40Z

Review comments:
diff_hunk: @@ -970,9 +968,6 @@ HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags,
                             // back to source lines for functions on stacks is very useful and we don't
                             // want to allow the function to fail for all targets.
 
-                            // Pulls in sequence points and local variable info
-                            DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMethodDesc);
path: src/coreclr/debug/daccess/enummem.cpp
user: login: max-charlamb
body: These shouldn't be required as they are called through `MethodDesc::EnumMemoryRegions`
created_at: 2025-05-14T18:16:58Z
author_association: MEMBER
diff_hunk: @@ -4164,7 +4170,7 @@ void CodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJitManager* pJ
 // Enumerate for minidumps.
 //-----------------------------------------------------------------------------
 template<typename TCodeHeader>
-void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
path: src/coreclr/vm/codeman.cpp
user: login: kg
body: Do we have any guarantees that pCodeInfo will be non-NULL entering this function?
created_at: 2025-05-14T19:07:10Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
+            EECodeInfo codeInfo(addrCode);
+            DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, &codeInfo);
+        }
+    }
+#else
+    PCODE entryPoint = GetNativeCode();
+    EECodeInfo codeInfo(entryPoint);
+    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, &entryPoint);
path: src/coreclr/vm/method.cpp
user: login: kg
body: This looks like it should be `&codeInfo` not `&entryPoint`
created_at: 2025-05-14T19:08:45Z
author_association: MEMBER
diff_hunk: @@ -4164,7 +4170,7 @@ void CodeHeader::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, IJitManager* pJ
 // Enumerate for minidumps.
 //-----------------------------------------------------------------------------
 template<typename TCodeHeader>
-void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, MethodDesc * pMD)
+void EECodeGenManager::EnumMemoryRegionsForMethodDebugInfoWorker(CLRDataEnumMemoryFlags flags, EECodeInfo * pCodeInfo)
path: src/coreclr/vm/codeman.cpp
user: login: kg
body: I see it's implicitly guaranteed in the caller's caller further down in the diff
created_at: 2025-05-14T19:09:01Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
path: src/coreclr/vm/method.cpp
user: login: kg
body: Are we missing an `iter->IsActiveChildVersion()`? I see it in some other callers of this iterator API
created_at: 2025-05-14T19:14:20Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
+            EECodeInfo codeInfo(addrCode);
path: src/coreclr/vm/method.cpp
user: login: kg
body: addrCode can be null, according to functioninfo.cpp, i quote:
```c++
            // Some versions may not be compiled yet - skip those for now
            // if they compile later the JitCompiled callback will add a DJI to our cache at that time
            PCODE codeAddr = itr->GetNativeCode();
```
created_at: 2025-05-14T19:18:37Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
path: src/coreclr/vm/method.cpp
user: login: kg
body: I think we might need a `CodeVersionManager::LockHolder codeVersioningLockHolder` here? `ProfToEEInterfaceImpl::GetNativeCodeStartAddresses` has one, among other users of this
created_at: 2025-05-14T19:22:21Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
path: src/coreclr/vm/method.cpp
user: login: max-charlamb
body: I'm pretty sure we want all of the versions, not just the active version because there could be callers inside an old version.
created_at: 2025-05-14T19:31:26Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
+            EECodeInfo codeInfo(addrCode);
path: src/coreclr/vm/method.cpp
user: login: max-charlamb
body: That should be safe. The `EECodeInfo` initialization will find that the `null` addrCode is Invalid which is verified in `DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo`.

I could change this to have an earlier check. Do you have a preference?
created_at: 2025-05-14T19:33:25Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
+        NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(dac_cast<PTR_MethodDesc>(this));
+        for (NativeCodeVersionIterator iter = nativeCodeVersions.Begin(); iter != nativeCodeVersions.End(); iter++)
+        {
+            PCODE addrCode = iter->GetNativeCode();
+            EECodeInfo codeInfo(addrCode);
path: src/coreclr/vm/method.cpp
user: login: kg
body: If EECodeInfo is going to do the null check and the callee will handle an invalid codeinfo that's good enough for me.
created_at: 2025-05-14T19:34:27Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
path: src/coreclr/vm/method.cpp
user: login: max-charlamb
body: I'm not sure if this is required. It looks like that code is in the runtime whereas this would only run in the DAC. I think the code versions should not be modified while the DAC is running.
created_at: 2025-05-14T19:35:54Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
path: src/coreclr/vm/method.cpp
user: login: kg
body: I was thinking the DAC probably only runs while the debuggee is paused (if I understand right), in which case you shouldn't need a lock, yeah. Though if someone is in the middle of mutating the data structure with the lock held, the DAC may see it in an incoherent/corrupted state?
created_at: 2025-05-14T19:56:33Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
path: src/coreclr/vm/method.cpp
user: login: davidwrighton
body: The DAC dealing with corrupted and messed up state is normal, but writing the code to take the lock is always the right choice, since it at least documents that it might be an issue, and the DAC will automatically drop the actual lock code. Over time, I want us to find these places where the DAC logically needs a lock for the purpose of reading and make it so that those cases which the DAC actually does can be written as lock-free code that can operate in proc or out of proc, but that's a longer term effort we will want to engage in. It turns out that the versions here almost work like that, the only issue is making sure a linked list is somewhat carefully constructed, which I'm sure we do yet. I've written that code, and tried to spread the lock-free world in a bunch of places, but that PR grew out of control, and I abandoned it. However, a scoped version of it would probably be a good idea, and also fairly easy to recreate.
created_at: 2025-05-14T20:47:20Z
author_association: MEMBER
diff_hunk: @@ -3673,7 +3673,22 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
     }
 
     // Need to save the Debug-Info for this method so that we can see it in a debugger later.
-    DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this);
+#ifdef FEATURE_CODE_VERSIONING
+    {
+        CodeVersionManager* pCodeVersionManager = GetCodeVersionManager();
path: src/coreclr/vm/method.cpp
user: login: max-charlamb
body: Modified to take the `CodeVersionManager::LockHolder codeVersioningLockHolder`
created_at: 2025-05-15T17:07:07Z
author_association: MEMBER

PR comments:
user: login: dotnet-policy-service[bot]
created_at: 2025-05-14T18:16:32Z
body: Tagging subscribers to this area: @tommcdon
See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
<!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
author_association: CONTRIBUTOR
user: login: max-charlamb
created_at: 2025-05-15T17:10:21Z
body: /azp run runtime-diagnostics
author_association: MEMBER
user: login: azure-pipelines[bot]
created_at: 2025-05-15T17:10:36Z
body: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>
author_association: NONE