Skip to content
Closed

test #127489

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
26 changes: 25 additions & 1 deletion src/coreclr/debug/createdump/crashinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,33 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(DumpType dumpType)
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED (%d %d)\n", m_enumMemoryPagesAdded, m_dataTargetPagesAdded);

// CLRDATA_ENUM_MEM_HEAP2 skips the expensive (in both time and memory usage) enumeration of the
// low level data structures and adds all the loader allocator heaps instead.
// low level data structures and adds all the loader allocator heaps instead. The older 'DbgEnableFastHeapDumps'
// env var didn't generate a complete enough heap dump on Linux and this new path does.
CLRDataEnumMemoryFlags flags = CLRDATA_ENUM_MEM_HEAP2;
MINIDUMP_TYPE minidumpType = GetMiniDumpType(dumpType);
if (dumpType == DumpType::Heap)
{
// This is the old fast heap env var for backwards compatibility for VS4Mac.
CLRConfigNoCache fastHeapDumps = CLRConfigNoCache::Get("DbgEnableFastHeapDumps", /*noprefix*/ false, &getenv);
DWORD val = 0;
if (fastHeapDumps.IsSet() && fastHeapDumps.TryAsInteger(10, val) && val == 1)
{
// Since on MacOS all the RW regions will be added for heap dumps by createdump, the
// only thing differentiating a MiniDumpNormal and a MiniDumpWithPrivateReadWriteMemory
// is that the later uses the EnumMemoryRegions APIs. This is kind of expensive on larger
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment typo: "the later uses" should be "the latter uses" (referring to MiniDumpWithPrivateReadWriteMemory vs MiniDumpNormal).

Suggested change
// is that the later uses the EnumMemoryRegions APIs. This is kind of expensive on larger
// is that the latter uses the EnumMemoryRegions APIs. This is kind of expensive on larger

Copilot uses AI. Check for mistakes.
// applications (4 minutes, or even more), and this should already be in RW pages. Change
// the dump type to the faster normal one. This one already ensures necessary DAC globals,
// etc. without the costly assembly, module, class, type runtime data structures enumeration.
minidumpType = MiniDumpNormal;
flags = CLRDATA_ENUM_MEM_DEFAULT;
}
// This env var allows the CLRDATA_ENUM_MEM_HEAP2 fast path to be opt-ed out
fastHeapDumps = CLRConfigNoCache::Get("EnableFastHeapDumps", /*noprefix*/ false, &getenv);
if (fastHeapDumps.IsSet() && fastHeapDumps.TryAsInteger(10, val) && val == 0)
{
flags = CLRDATA_ENUM_MEM_DEFAULT;
}
}
// Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC
HRESULT hr = m_pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, flags);
if (FAILED(hr))
Expand Down
109 changes: 82 additions & 27 deletions src/coreclr/debug/daccess/enummem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1861,51 +1861,97 @@ HRESULT ClrDataAccess::EnumMemWriteDataSegment()

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Custom dumps enumerate the minimal CLR state needed for
// MiniDumpWithFullAuxiliaryState: thread stacks, modules, CLR statics, and any
// memory reached implicitly from those roots.
// Custom Dump. Depending on the value of g_ECustomDumpFlavor, different dump
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment grammar: "different dump will be taken" should be plural (e.g., "different dumps will be taken").

Suggested change
// Custom Dump. Depending on the value of g_ECustomDumpFlavor, different dump
// Custom Dump. Depending on the value of g_ECustomDumpFlavor, different dumps

Copilot uses AI. Check for mistakes.
// will be taken. You can set this global variable using hosting API
// ICLRErrorReportingManager::BeginCustomDump.
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
HRESULT ClrDataAccess::EnumMemoryRegionsWorkerCustom()
{
SUPPORTS_DAC;

HRESULT status = S_OK;

ECustomDumpFlavor eFlavor;

eFlavor = DUMP_FLAVOR_Default;

m_enumMemFlags = CLRDATA_ENUM_MEM_MINI;

// clear all of the previous cached memory
Flush();

// Iterating to all threads' stacks
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); )
if (FAILED(status))
if (eFlavor == DUMP_FLAVOR_Mini)
{
return status;
}
// Iterating to all threads' stacks
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); )

// Iterating to module list.
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); )
if (FAILED(status))
{
return status;
}
// Iterating to module list.
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); )

//
// iterating through static that we care
//
// collect CLR static
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); )
if (FAILED(status))
//
// iterating through static that we care
//
// collect CLR static
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); )

// we are done...

// now dump the memory get dragged in implicitly
m_dumpStats.m_cbImplicitly = m_instances.DumpAllInstances(m_enumMemCb);

}
else if (eFlavor == DUMP_FLAVOR_CriticalCLRState)
{
return status;
// We need to walk Threads stack to view managed frames.
// Iterating through module list

// Iterating to all threads' stacks
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); )

// Iterating to module list.
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); )

//
// iterating through static that we care
//
// collect CLR static
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); )

// Collecting some CLR secondary critical data
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); )

// we are done...

// now dump the memory get dragged in implicitly
m_dumpStats.m_cbImplicitly = m_instances.DumpAllInstances(m_enumMemCb);

}
else if (eFlavor == DUMP_FLAVOR_NonHeapCLRState)
{
// since all CLR hosted heap will be dump by the host,
// the EE structures that are not loaded using LoadLibrary will
// be included by the host.
//
// Thus we only need to include mscorwks's critical data and ngen images

// we are done...
m_enumMemFlags = CLRDATA_ENUM_MEM_HEAP;

// now dump the memory get dragged in implicitly
m_dumpStats.m_cbImplicitly = m_instances.DumpAllInstances(m_enumMemCb);
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); )

return status;
// Collecting some CLR secondary critical data
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); )

CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); )
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCollectImages(); )
}
else
{
status = E_INVALIDARG;
}

return S_OK;
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnumMemoryRegionsWorkerCustom computes a "status" value (including setting E_INVALIDARG), but unconditionally returns S_OK. This will mask failures/cancellation from callers and can make dump generation report success even when enumeration failed. Return the computed status instead of always returning S_OK.

Suggested change
return S_OK;
return status;

Copilot uses AI. Check for mistakes.
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Expand Down Expand Up @@ -1949,7 +1995,7 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWrapper(IN CLRDataEnumMemoryFlags flags)
// triage micro-dump
status = EnumMemoryRegionsWorkerMicroTriage(flags);
}
else if (flags == CLRDATA_ENUM_MEM_HEAP2)
else if (flags == CLRDATA_ENUM_MEM_HEAP || flags == CLRDATA_ENUM_MEM_HEAP2)
{
status = EnumMemoryRegionsWorkerHeap(flags);
}
Expand Down Expand Up @@ -2041,7 +2087,16 @@ ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback,
ClearDumpStats();
if (miniDumpFlags & MiniDumpWithPrivateReadWriteMemory)
{
status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_HEAP2);
// heap dump
if (flags == CLRDATA_ENUM_MEM_HEAP2)
{
DacLogMessage("EnumMemoryRegions(CLRDATA_ENUM_MEM_HEAP2)\n");
}
else
{
flags = CLRDATA_ENUM_MEM_HEAP;
}
status = EnumMemoryRegionsWrapper(flags);
}
else if (miniDumpFlags & MiniDumpWithFullAuxiliaryState)
{
Expand Down
23 changes: 23 additions & 0 deletions src/coreclr/debug/ee/functioninfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2435,6 +2435,16 @@ DebuggerMethodInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
DAC_ENUM_DTHIS();
SUPPORTS_DAC;

if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
// Modules are enumerated already for minidumps, save the empty calls.
if (m_module.IsValid())
{
m_module->EnumMemoryRegions(flags, true);
}

}

Comment on lines +2438 to +2447
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DebuggerMethodInfoEntry::EnumMemoryRegions already enumerates key.pModule when flags are not MINI/TRIAGE/HEAP2. The newly added module enumeration in DebuggerMethodInfo::EnumMemoryRegions causes the same module to be enumerated again for each DebuggerMethodInfo reached from an entry, adding redundant work/size. Consider removing one of the two module-enumeration sites (preferably keep it at the entry level).

Suggested change
if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
// Modules are enumerated already for minidumps, save the empty calls.
if (m_module.IsValid())
{
m_module->EnumMemoryRegions(flags, true);
}
}

Copilot uses AI. Check for mistakes.
PTR_DebuggerJitInfo jitInfo = m_latestJitInfo;
while (jitInfo.IsValid())
{
Expand All @@ -2454,6 +2464,19 @@ DebuggerJitInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
m_methodInfo->EnumMemoryRegions(flags);
}

if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
if (m_nativeCodeVersion.GetMethodDesc().IsValid())
{
m_nativeCodeVersion.GetMethodDesc()->EnumMemoryRegions(flags);
}

DacEnumMemoryRegion(PTR_TO_TADDR(GetSequenceMap()),
GetSequenceMapCount() * sizeof(DebuggerILToNativeMap));
DacEnumMemoryRegion(PTR_TO_TADDR(GetVarNativeInfo()),
GetVarNativeInfoCount() *
sizeof(ICorDebugInfo::NativeVarInfo));
}
}


Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/vm/assembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,21 @@ Assembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
GetLoaderAllocator()->EnumMemoryRegions(flags);
}
else
{
if (m_pClassLoader.IsValid())
{
m_pClassLoader->EnumMemoryRegions(flags);
}
if (m_pModule.IsValid())
{
m_pModule->EnumMemoryRegions(flags, true);
}
Comment on lines +1890 to +1893
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assembly::EnumMemoryRegions now calls m_pModule->EnumMemoryRegions(), but Module::EnumMemoryRegions also calls m_pAssembly->EnumMemoryRegions(). For a typical assembly/manifest module pair this creates mutual recursion and can lead to unbounded recursion during DAC memory enumeration. Consider removing the module enumeration from Assembly::EnumMemoryRegions (or otherwise breaking the cycle, e.g., a helper that doesn't recurse back to the Assembly).

Suggested change
if (m_pModule.IsValid())
{
m_pModule->EnumMemoryRegions(flags, true);
}

Copilot uses AI. Check for mistakes.
if (m_pPEAssembly.IsValid())
{
m_pPEAssembly->EnumMemoryRegions(flags);
}
}
}

#else // DACCESS_COMPILE
Expand Down
73 changes: 73 additions & 0 deletions src/coreclr/vm/ceeload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4522,6 +4522,79 @@ void Module::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
{
GetLoaderAllocator()->EnumMemoryRegions(flags);
}
else if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE)
{
if (m_pAvailableClasses.IsValid())
{
m_pAvailableClasses->EnumMemoryRegions(flags);
}
if (m_pAvailableParamTypes.IsValid())
{
m_pAvailableParamTypes->EnumMemoryRegions(flags);
}
if (m_pInstMethodHashTable.IsValid())
{
m_pInstMethodHashTable->EnumMemoryRegions(flags);
}
if (m_pAvailableClassesCaseIns.IsValid())
{
m_pAvailableClassesCaseIns->EnumMemoryRegions(flags);
}

// Save the LookupMap structures.
m_MethodDefToDescMap.ListEnumMemoryRegions(flags);
m_FieldDefToDescMap.ListEnumMemoryRegions(flags);
m_MemberRefMap.ListEnumMemoryRegions(flags);
m_GenericParamToDescMap.ListEnumMemoryRegions(flags);
m_ManifestModuleReferencesMap.ListEnumMemoryRegions(flags);

LookupMap<PTR_MethodTable>::Iterator typeDefIter(&m_TypeDefToMethodTableMap);
while (typeDefIter.Next())
{
if (typeDefIter.GetElement())
{
typeDefIter.GetElement()->EnumMemoryRegions(flags);
}
}

LookupMap<PTR_TypeRef>::Iterator typeRefIter(&m_TypeRefToMethodTableMap);
while (typeRefIter.Next())
{
if (typeRefIter.GetElement())
{
TypeHandle th = TypeHandle::FromTAddr(dac_cast<TADDR>(typeRefIter.GetElement()));
th.EnumMemoryRegions(flags);
}
}

LookupMap<PTR_MethodDesc>::Iterator methodDefIter(&m_MethodDefToDescMap);
while (methodDefIter.Next())
{
if (methodDefIter.GetElement())
{
methodDefIter.GetElement()->EnumMemoryRegions(flags);
}
}

LookupMap<PTR_FieldDesc>::Iterator fieldDefIter(&m_FieldDefToDescMap);
while (fieldDefIter.Next())
{
if (fieldDefIter.GetElement())
{
fieldDefIter.GetElement()->EnumMemoryRegions(flags);
}
}

LookupMap<PTR_TypeVarTypeDesc>::Iterator genericParamIter(&m_GenericParamToDescMap);
while (genericParamIter.Next())
{
if (genericParamIter.GetElement())
{
genericParamIter.GetElement()->EnumMemoryRegions(flags);
}
}

} // !CLRDATA_ENUM_MEM_MINI && !CLRDATA_ENUM_MEM_TRIAGE && !CLRDATA_ENUM_MEM_HEAP2

LookupMap<PTR_Module>::Iterator asmRefIter(&m_ManifestModuleReferencesMap);
while (asmRefIter.Next())
Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/vm/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3369,6 +3369,21 @@ EEClass::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, MethodTable * pMT)
if (HasOptionalFields())
DacEnumMemoryRegion(dac_cast<TADDR>(GetOptionalFields()), sizeof(EEClassOptionalFields));

if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
PTR_Module pModule = pMT->GetModule();
if (pModule.IsValid())
{
pModule->EnumMemoryRegions(flags, true);
}
PTR_MethodDescChunk chunk = GetChunks();
while (chunk.IsValid())
{
chunk->EnumMemoryRegions(flags);
chunk = chunk->GetNextChunk();
}
}

PTR_FieldDesc pFieldDescList = GetFieldDescList();
if (pFieldDescList.IsValid())
{
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7788,6 +7788,14 @@ MethodTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
}
}

if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
DispatchMap * pMap = GetDispatchMap();
if (pMap != NULL)
{
pMap->EnumMemoryRegions(flags);
}
}
} // MethodTable::EnumMemoryRegions

#endif // DACCESS_COMPILE
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/vm/threads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6784,6 +6784,10 @@ Thread::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
WRAPPER_NO_CONTRACT;

DAC_ENUM_DTHIS();
if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
AppDomain::GetCurrentDomain()->EnumMemoryRegions(flags, true);
}

if (m_debuggerFilterContext.IsValid())
{
Expand Down Expand Up @@ -6871,6 +6875,11 @@ Thread::EnumMemoryRegionsWorker(CLRDataEnumMemoryFlags flags)
DacGetThreadContext(this, &context);
}

if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
AppDomain::GetCurrentDomain()->EnumMemoryRegions(flags, true);
}

Comment on lines +6878 to +6882
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppDomain::EnumMemoryRegions is now invoked both in Thread::EnumMemoryRegions and again in Thread::EnumMemoryRegionsWorker for the same flags. Since Worker is always called from EnumMemoryRegions, this duplicates AppDomain/assembly/module enumeration work and can significantly increase dump size/time. Only one of these calls should remain (likely keep the top-level call and remove the Worker call).

Suggested change
if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE && flags != CLRDATA_ENUM_MEM_HEAP2)
{
AppDomain::GetCurrentDomain()->EnumMemoryRegions(flags, true);
}

Copilot uses AI. Check for mistakes.
FillRegDisplay(&regDisp, &context);
frameIter.Init(this, NULL, &regDisp, 0);
while (frameIter.IsValid())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out
try
{
Guid iidMetaDataImport = typeof(IMetaDataImport).GUID;
if (_legacyModulePointer != 0 && Marshal.QueryInterface(_legacyModulePointer, iidMetaDataImport, out nint ppMdi) >= 0)
if (_legacyModulePointer != 0 && _legacyModulePointer != 1 && Marshal.QueryInterface(_legacyModulePointer, iidMetaDataImport, out nint ppMdi) >= 0)
{
legacyImport = ComInterfaceMarshaller<IMetaDataImport>.ConvertToManaged((void*)ppMdi);
Marshal.Release(ppMdi);
Expand Down
Loading