From 5a147beef24be31e769d3f05493350bbc3d67761 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 4 Nov 2025 18:48:41 -0500 Subject: [PATCH 1/6] save datadescriptors in minidumps --- src/coreclr/debug/daccess/CMakeLists.txt | 1 + src/coreclr/debug/daccess/dacimpl.h | 1 + src/coreclr/debug/daccess/enummem.cpp | 75 ++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/src/coreclr/debug/daccess/CMakeLists.txt b/src/coreclr/debug/daccess/CMakeLists.txt index 747a7aae6f2333..37af298156ea31 100644 --- a/src/coreclr/debug/daccess/CMakeLists.txt +++ b/src/coreclr/debug/daccess/CMakeLists.txt @@ -8,6 +8,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CLR_DIR}/debug/ee) include_directories(${CLR_DIR}/gcdump) include_directories(${CLR_DIR}/interop/inc) +include_directories(${CLR_DIR}/debug/datadescriptor-shared/inc) if(CLR_CMAKE_HOST_UNIX) include_directories(${GENERATED_INCLUDE_DIR}) diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index cfab4de3d055bb..e2f867a8315fd4 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1337,6 +1337,7 @@ class ClrDataAccess HRESULT EnumMemDumpAppDomainInfo(CLRDataEnumMemoryFlags flags); HRESULT EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags); HRESULT EnumMemCLRMainModuleInfo(); + HRESULT EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags); bool ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess = true); bool DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer); diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 828ae77d6aae16..01d7259caa0c96 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -26,6 +26,7 @@ #endif // FEATURE_COMWRAPPERS #include "cdacplatformmetadata.hpp" +#include "contract-descriptor.h" extern HRESULT GetDacTableAddress(ICorDebugDataTarget* dataTarget, ULONG64 baseAddress, PULONG64 dacTableAddress); @@ -1631,6 +1632,77 @@ HRESULT ClrDataAccess::EnumMemCLRMainModuleInfo() return status; } +typedef DPTR(ContractDescriptor) PTR_ContractDescriptor; +extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress); + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// +// Reports datadescriptor data for the cDAC +// that needs to be present in all minidumps. +// Hardcodes number of subdescriptors and does not recursively enumerate them. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + HRESULT status = S_OK; + + uint64_t contractDescriptorAddr = 0; + if (!TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr)) + { + return E_FAIL; + } + + PTR_ContractDescriptor pContractDescriptor = dac_cast((TADDR)contractDescriptorAddr); + // Enumerate the ContractDescriptor structure + ReportMem(dac_cast(pContractDescriptor), sizeof(ContractDescriptor)); + // Report the pointer data array + ReportMem((TADDR)pContractDescriptor->pointer_data, pContractDescriptor->pointer_data_count * sizeof(void*)); + // Report the JSON blob + ReportMem((TADDR)pContractDescriptor->descriptor, pContractDescriptor->descriptor_size); + + // Assume that subdescriptors will be the n last pointers in the pointer_data array + // Must be updated if further subdescriptors are added + // This could be improved by iterating all of the pointer data recursively and identifying subdescriptors by + // the magic field in ContractDescriptor, but given it will eventually move to the cDAC, this is good enough for now. + int cSubDescriptors = 1; + for (int i = 0; i < cSubDescriptors; i++) + { + int subDescriptorIndex = (pContractDescriptor->pointer_data_count - 1) - i; + + TADDR pSubDescriptorAddr; + ULONG32 bytesRead; + if (FAILED(m_pTarget->ReadVirtual( + (TADDR)pContractDescriptor->pointer_data + subDescriptorIndex * sizeof(void*), + (PBYTE)&pSubDescriptorAddr, sizeof(TADDR), &bytesRead)) + || bytesRead != sizeof(TADDR) + || pSubDescriptorAddr == NULL) + { + continue; + } + + TADDR subDescriptorAddr; + if (FAILED(m_pTarget->ReadVirtual( + pSubDescriptorAddr, + (PBYTE)&subDescriptorAddr, sizeof(TADDR), &bytesRead)) + || bytesRead != sizeof(TADDR) + || subDescriptorAddr == NULL) + { + continue; + } + + PTR_ContractDescriptor pSubDescriptor = dac_cast(subDescriptorAddr); + // Enumerate the ContractDescriptor structure + ReportMem(dac_cast(pSubDescriptor), sizeof(ContractDescriptor)); + // Report the pointer data array + ReportMem((TADDR)pSubDescriptor->pointer_data, pSubDescriptor->pointer_data_count * sizeof(void*)); + // Report the JSON blob + ReportMem((TADDR)pSubDescriptor->descriptor, pSubDescriptor->descriptor_size); + } + + return status; +} //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // @@ -2059,6 +2131,9 @@ ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback, } } #endif + + EnumMemDataDescriptors(flags); + Flush(); } EX_CATCH From 126eacfe272b666c965e8be4d6e9e022631abcb1 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 4 Nov 2025 18:50:46 -0500 Subject: [PATCH 2/6] add comment --- src/coreclr/vm/datadescriptor/datadescriptor.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 0d023bf3340c46..a483de93aa0acf 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1144,6 +1144,9 @@ CDAC_GLOBAL_POINTER(MethodDescSizeTable, &MethodDesc::s_ClassificationSizeTable) CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address) CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address) + +// It is important for the subdescriptor pointers to be the last pointers in the global structure +// enummem.cpp relies on this order to find and enumerate the subdescriptors CDAC_GLOBAL_SUB_DESCRIPTOR(GC, &(g_gc_dac_vars.gc_descriptor)) CDAC_GLOBAL_CONTRACT(CodeVersions, 1) From c608b9d0d9d127953205c12af46d42049de44b17 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 5 Nov 2025 10:37:39 -0500 Subject: [PATCH 3/6] null -> 0 --- src/coreclr/debug/daccess/enummem.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 01d7259caa0c96..c17103fa220d0d 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -1677,7 +1677,7 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) (TADDR)pContractDescriptor->pointer_data + subDescriptorIndex * sizeof(void*), (PBYTE)&pSubDescriptorAddr, sizeof(TADDR), &bytesRead)) || bytesRead != sizeof(TADDR) - || pSubDescriptorAddr == NULL) + || pSubDescriptorAddr == 0) { continue; } @@ -1687,7 +1687,7 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) pSubDescriptorAddr, (PBYTE)&subDescriptorAddr, sizeof(TADDR), &bytesRead)) || bytesRead != sizeof(TADDR) - || subDescriptorAddr == NULL) + || subDescriptorAddr == 0) { continue; } From 5653f94e90d1a5842b15a62331beeaef137767e3 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:25:56 -0500 Subject: [PATCH 4/6] use a helper method to enumerate the ContractDescriptor structure --- src/coreclr/debug/daccess/dacimpl.h | 2 ++ src/coreclr/debug/daccess/enummem.cpp | 36 +++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index e2f867a8315fd4..4a86846a89d913 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1339,6 +1339,8 @@ class ClrDataAccess HRESULT EnumMemCLRMainModuleInfo(); HRESULT EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags); + void EnumDataDescriptorHelper(TADDR dataDescriptorAddr); + bool ReportMem(TADDR addr, TSIZE_T size, bool fExpectSuccess = true); bool DacUpdateMemoryRegion(TADDR addr, TSIZE_T bufferSize, BYTE* buffer); diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index c17103fa220d0d..5ca15a2fa278ae 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -1637,8 +1637,7 @@ extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddre //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // -// Reports datadescriptor data for the cDAC -// that needs to be present in all minidumps. +// Reports datadescriptor data for the cDAC. // Hardcodes number of subdescriptors and does not recursively enumerate them. // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -1650,22 +1649,16 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) uint64_t contractDescriptorAddr = 0; if (!TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr)) - { return E_FAIL; - } - PTR_ContractDescriptor pContractDescriptor = dac_cast((TADDR)contractDescriptorAddr); - // Enumerate the ContractDescriptor structure - ReportMem(dac_cast(pContractDescriptor), sizeof(ContractDescriptor)); - // Report the pointer data array - ReportMem((TADDR)pContractDescriptor->pointer_data, pContractDescriptor->pointer_data_count * sizeof(void*)); - // Report the JSON blob - ReportMem((TADDR)pContractDescriptor->descriptor, pContractDescriptor->descriptor_size); + // Save the main descriptor + EnumDataDescriptorHelper((TADDR)contractDescriptorAddr); // Assume that subdescriptors will be the n last pointers in the pointer_data array // Must be updated if further subdescriptors are added // This could be improved by iterating all of the pointer data recursively and identifying subdescriptors by // the magic field in ContractDescriptor, but given it will eventually move to the cDAC, this is good enough for now. + PTR_ContractDescriptor pContractDescriptor = dac_cast((TADDR)contractDescriptorAddr); int cSubDescriptors = 1; for (int i = 0; i < cSubDescriptors; i++) { @@ -1692,18 +1685,25 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) continue; } - PTR_ContractDescriptor pSubDescriptor = dac_cast(subDescriptorAddr); - // Enumerate the ContractDescriptor structure - ReportMem(dac_cast(pSubDescriptor), sizeof(ContractDescriptor)); - // Report the pointer data array - ReportMem((TADDR)pSubDescriptor->pointer_data, pSubDescriptor->pointer_data_count * sizeof(void*)); - // Report the JSON blob - ReportMem((TADDR)pSubDescriptor->descriptor, pSubDescriptor->descriptor_size); + // Save the subdescriptor + EnumDataDescriptorHelper(subDescriptorAddr); } return status; } +void ClrDataAccess::EnumDataDescriptorHelper(TADDR dataDescriptorAddr) +{ + PTR_ContractDescriptor pDataDescriptor = dac_cast(dataDescriptorAddr); + + // Enumerate the ContractDescriptor structure + ReportMem(dac_cast(pDataDescriptor), sizeof(ContractDescriptor)); + // Report the pointer data array + ReportMem((TADDR)pDataDescriptor->pointer_data, pDataDescriptor->pointer_data_count * sizeof(void*)); + // Report the JSON blob + ReportMem((TADDR)pDataDescriptor->descriptor, pDataDescriptor->descriptor_size); +} + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Generating skinny mini-dump. Skinny mini-dump will only support stack trace, module list, From 4c4bc6175a9e855baa1e9e915d0b56e346233ba9 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:31:01 -0500 Subject: [PATCH 5/6] update comments --- src/coreclr/debug/daccess/enummem.cpp | 4 ++-- src/coreclr/vm/datadescriptor/datadescriptor.inc | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 5ca15a2fa278ae..e96c3964ccb0e2 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -1657,9 +1657,9 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) // Assume that subdescriptors will be the n last pointers in the pointer_data array // Must be updated if further subdescriptors are added // This could be improved by iterating all of the pointer data recursively and identifying subdescriptors by - // the magic field in ContractDescriptor, but given it will eventually move to the cDAC, this is good enough for now. - PTR_ContractDescriptor pContractDescriptor = dac_cast((TADDR)contractDescriptorAddr); + // the magic field in ContractDescriptor. Given the low number of subdescriptors, this is not necessary right now. int cSubDescriptors = 1; + PTR_ContractDescriptor pContractDescriptor = dac_cast((TADDR)contractDescriptorAddr); for (int i = 0; i < cSubDescriptors; i++) { int subDescriptorIndex = (pContractDescriptor->pointer_data_count - 1) - i; diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index a483de93aa0acf..f9377a1f6fbac9 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1145,8 +1145,11 @@ CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address) CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address) -// It is important for the subdescriptor pointers to be the last pointers in the global structure -// enummem.cpp relies on this order to find and enumerate the subdescriptors +// It is important for the subdescriptor pointers to be the last pointers in the global structure. +// EnumMemDataDescriptors in enummem.cpp hardcodes the number of subdescriptors and utilizes +// this order to find and enumerate the subdescriptors. +// +// When adding a new subdescriptor, EnumMemDescriptors must be updated appropriately. CDAC_GLOBAL_SUB_DESCRIPTOR(GC, &(g_gc_dac_vars.gc_descriptor)) CDAC_GLOBAL_CONTRACT(CodeVersions, 1) From 62e095816017bab2058e1cd9876c2d60f9e0fa6c Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Fri, 7 Nov 2025 11:35:09 -0500 Subject: [PATCH 6/6] add try/catch --- src/coreclr/debug/daccess/enummem.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index e96c3964ccb0e2..88737aee527c82 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -1645,8 +1645,6 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) { SUPPORTS_DAC; - HRESULT status = S_OK; - uint64_t contractDescriptorAddr = 0; if (!TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr)) return E_FAIL; @@ -1689,19 +1687,23 @@ HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) EnumDataDescriptorHelper(subDescriptorAddr); } - return status; + return S_OK; } void ClrDataAccess::EnumDataDescriptorHelper(TADDR dataDescriptorAddr) { PTR_ContractDescriptor pDataDescriptor = dac_cast(dataDescriptorAddr); - // Enumerate the ContractDescriptor structure - ReportMem(dac_cast(pDataDescriptor), sizeof(ContractDescriptor)); - // Report the pointer data array - ReportMem((TADDR)pDataDescriptor->pointer_data, pDataDescriptor->pointer_data_count * sizeof(void*)); - // Report the JSON blob - ReportMem((TADDR)pDataDescriptor->descriptor, pDataDescriptor->descriptor_size); + EX_TRY + { + // Enumerate the ContractDescriptor structure + ReportMem(dac_cast(pDataDescriptor), sizeof(ContractDescriptor)); + // Report the pointer data array + ReportMem((TADDR)pDataDescriptor->pointer_data, pDataDescriptor->pointer_data_count * sizeof(void*)); + // Report the JSON blob + ReportMem((TADDR)pDataDescriptor->descriptor, pDataDescriptor->descriptor_size); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++