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..4a86846a89d913 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1337,6 +1337,9 @@ class ClrDataAccess HRESULT EnumMemDumpAppDomainInfo(CLRDataEnumMemoryFlags flags); HRESULT EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags); 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 828ae77d6aae16..88737aee527c82 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,79 @@ 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. +// Hardcodes number of subdescriptors and does not recursively enumerate them. +// +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +HRESULT ClrDataAccess::EnumMemDataDescriptors(CLRDataEnumMemoryFlags flags) +{ + SUPPORTS_DAC; + + uint64_t contractDescriptorAddr = 0; + if (!TryGetSymbol(m_pTarget, m_globalBase, "DotNetRuntimeContractDescriptor", &contractDescriptorAddr)) + return E_FAIL; + + // 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. 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; + + 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 == 0) + { + continue; + } + + TADDR subDescriptorAddr; + if (FAILED(m_pTarget->ReadVirtual( + pSubDescriptorAddr, + (PBYTE)&subDescriptorAddr, sizeof(TADDR), &bytesRead)) + || bytesRead != sizeof(TADDR) + || subDescriptorAddr == 0) + { + continue; + } + + // Save the subdescriptor + EnumDataDescriptorHelper(subDescriptorAddr); + } + + return S_OK; +} + +void ClrDataAccess::EnumDataDescriptorHelper(TADDR dataDescriptorAddr) +{ + PTR_ContractDescriptor pDataDescriptor = dac_cast(dataDescriptorAddr); + + 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 +} //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // @@ -2059,6 +2133,9 @@ ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback, } } #endif + + EnumMemDataDescriptors(flags); + Flush(); } EX_CATCH diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 0d023bf3340c46..f9377a1f6fbac9 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1144,6 +1144,12 @@ 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. +// 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)