diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index d9589fedb0e056..d60a73e8e24a92 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -67,9 +67,11 @@ struct cdac_data #endif // !USE_REGIONS /* For use in GCHeapAnalyzeData APIs */ +#ifdef HEAP_ANALYZE GC_HEAP_FIELD(InternalRootArray, internal_root_array) GC_HEAP_FIELD(InternalRootArrayIndex, internal_root_array_index) GC_HEAP_FIELD(HeapAnalyzeSuccess, heap_analyze_success) +#endif // HEAP_ANALYZE /* For use in GCInterestingInfo APIs */ GC_HEAP_FIELD(InterestingData, interesting_data_per_heap) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index 49243687f13f4b..2937aa7627eb8e 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -27,9 +27,11 @@ CDAC_TYPE_FIELD(GCHeap, T_POINTER, SavedSweepEphemeralSeg, cdac_data::SavedSweepEphemeralStart) #endif // !USE_REGIONS CDAC_TYPE_FIELD(GCHeap, TYPE(OomHistory), OomData, cdac_data::OomData) +#ifdef HEAP_ANALYZE CDAC_TYPE_FIELD(GCHeap, T_POINTER, InternalRootArray, cdac_data::InternalRootArray) CDAC_TYPE_FIELD(GCHeap, T_NUINT, InternalRootArrayIndex, cdac_data::InternalRootArrayIndex) CDAC_TYPE_FIELD(GCHeap, T_INT32, HeapAnalyzeSuccess, cdac_data::HeapAnalyzeSuccess) +#endif // HEAP_ANALYZE CDAC_TYPE_FIELD(GCHeap, T_POINTER, InterestingData, cdac_data::InterestingData) CDAC_TYPE_FIELD(GCHeap, T_POINTER, CompactReasons, cdac_data::CompactReasons) CDAC_TYPE_FIELD(GCHeap, T_POINTER, ExpandMechanisms, cdac_data::ExpandMechanisms) @@ -166,9 +168,11 @@ CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralSeg, cdac_data::SavedSweepEphemeralStart) #endif // !USE_REGIONS CDAC_GLOBAL_POINTER(GCHeapOomData, cdac_data::OomData) +#ifdef HEAP_ANALYZE CDAC_GLOBAL_POINTER(GCHeapInternalRootArray, cdac_data::InternalRootArray) CDAC_GLOBAL_POINTER(GCHeapInternalRootArrayIndex, cdac_data::InternalRootArrayIndex) CDAC_GLOBAL_POINTER(GCHeapHeapAnalyzeSuccess, cdac_data::HeapAnalyzeSuccess) +#endif // HEAP_ANALYZE CDAC_GLOBAL_POINTER(GCHeapInterestingData, cdac_data::InterestingData) CDAC_GLOBAL_POINTER(GCHeapCompactReasons, cdac_data::CompactReasons) CDAC_GLOBAL_POINTER(GCHeapExpandMechanisms, cdac_data::ExpandMechanisms) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 2c1e91afc8ba16..950cc3d538a236 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -236,6 +236,8 @@ The .NET Foundation licenses this file to you under the MIT license. + + diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index 9f3a80c702358e..058e4e20adde42 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -290,6 +290,9 @@ if(CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM) add_definitions(-DINTERFACE_DISPATCH_CACHE_HAS_CELL_BACKPOINTER) endif() add_definitions(-D_LIB) +if(NOT CLR_CMAKE_TARGET_ARCH_WASM) + add_definitions(-DGC_DESCRIPTOR) +endif() # there is a problem with undefined symbols when this is set # add_definitions(-DSTRESS_HEAP) @@ -346,3 +349,13 @@ endif() if(FEATURE_PERFTRACING) add_subdirectory(eventpipe) endif() + +if(NOT CLR_CMAKE_TARGET_ARCH_WASM) + # Create an interface library that captures the Runtime's include directories + # for use by the datadescriptor intermediary compilation. + add_library(nativeaot_runtime_includes INTERFACE) + get_property(_runtime_includes DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) + target_include_directories(nativeaot_runtime_includes INTERFACE ${_runtime_includes}) + + add_subdirectory(datadescriptor) +endif() diff --git a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp index 9c2a06892c629f..0f86c1e2383792 100644 --- a/src/coreclr/nativeaot/Runtime/DebugHeader.cpp +++ b/src/coreclr/nativeaot/Runtime/DebugHeader.cpp @@ -80,8 +80,9 @@ struct DotNetRuntimeDebugHeader // v1-v4 were never doc'ed but history is source control if you need it // v5 - Thread now has an m_eeAllocContext field and the previous m_rgbAllocContextBuffer // field is nested inside of it. + // v6 - Removed RuntimeInstance.m_pThreadStore field, added g_pThreadStore global. // - const uint16_t MajorVersion = 5; + const uint16_t MajorVersion = 6; // This counter can be incremented to indicate back-compatible changes // This field must be encoded little endian, regardless of the typical endianness of @@ -255,13 +256,10 @@ extern "C" void PopulateDebugHeaders() MAKE_SIZE_ENTRY(StressMsg); MAKE_DEBUG_FIELD_ENTRY(StressMsg, args); - MAKE_SIZE_ENTRY(RuntimeInstance); - MAKE_DEBUG_FIELD_ENTRY(RuntimeInstance, m_pThreadStore); - MAKE_GLOBAL_ENTRY(g_CrashInfoBuffer); - RuntimeInstance *g_pTheRuntimeInstance = GetRuntimeInstance(); - MAKE_GLOBAL_ENTRY(g_pTheRuntimeInstance); + ThreadStore *g_pThreadStore = ThreadStore::s_pThreadStore; + MAKE_GLOBAL_ENTRY(g_pThreadStore); MAKE_GLOBAL_ENTRY(g_gcDacGlobals); diff --git a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt index 74cdeca700a1ae..1cd73bfd07823d 100644 --- a/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/Full/CMakeLists.txt @@ -24,11 +24,11 @@ endif (CLR_CMAKE_TARGET_WIN32) add_library(Runtime.WorkstationGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) add_dependencies(Runtime.WorkstationGC aot_eventing_headers) -target_link_libraries(Runtime.WorkstationGC PRIVATE aotminipal) +target_link_libraries(Runtime.WorkstationGC PRIVATE aotminipal nativeaot_cdac_contract_descriptor nativeaot_gc_wks_descriptor) add_library(Runtime.ServerGC STATIC ${COMMON_RUNTIME_SOURCES} ${FULL_RUNTIME_SOURCES} ${SERVER_GC_SOURCES} ${RUNTIME_ARCH_ASM_OBJECTS}) add_dependencies(Runtime.ServerGC aot_eventing_headers) -target_link_libraries(Runtime.ServerGC PRIVATE aotminipal) +target_link_libraries(Runtime.ServerGC PRIVATE aotminipal nativeaot_cdac_contract_descriptor nativeaot_gc_wks_descriptor nativeaot_gc_svr_descriptor) add_library(standalonegc-disabled STATIC ${STANDALONEGC_DISABLED_SOURCES}) add_dependencies(standalonegc-disabled aot_eventing_headers) diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp index 74a930a0e9bdc8..141e53aa4af2e3 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp @@ -39,7 +39,7 @@ uint8_t g_CrashInfoBuffer[MAX_CRASHINFOBUFFER_SIZE] = { 0 }; ThreadStore * RuntimeInstance::GetThreadStore() { - return m_pThreadStore; + return ThreadStore::s_pThreadStore; } FCIMPL1(uint8_t *, RhGetCrashInfoBuffer, int32_t* pcbMaxSize) @@ -179,7 +179,6 @@ RuntimeInstance::OsModuleList* RuntimeInstance::GetOsModuleList() #ifndef DACCESS_COMPILE RuntimeInstance::RuntimeInstance() : - m_pThreadStore(NULL), m_CodeManager(NULL), m_conservativeStackReportingEnabled(false), m_pUnboxingStubsRegion(NULL) @@ -188,10 +187,10 @@ RuntimeInstance::RuntimeInstance() : RuntimeInstance::~RuntimeInstance() { - if (NULL != m_pThreadStore) + if (NULL != ThreadStore::s_pThreadStore) { - delete m_pThreadStore; - m_pThreadStore = NULL; + delete ThreadStore::s_pThreadStore; + ThreadStore::s_pThreadStore = NULL; } } @@ -314,11 +313,11 @@ bool RuntimeInstance::Initialize(HANDLE hPalInstance) pThreadStore.SuppressRelease(); pRuntimeInstance.SuppressRelease(); - pRuntimeInstance->m_pThreadStore = pThreadStore; pRuntimeInstance->m_hPalInstance = hPalInstance; ASSERT_MSG(g_pTheRuntimeInstance == NULL, "multi-instances are not supported"); g_pTheRuntimeInstance = pRuntimeInstance; + ThreadStore::s_pThreadStore = pThreadStore; return true; } diff --git a/src/coreclr/nativeaot/Runtime/RuntimeInstance.h b/src/coreclr/nativeaot/Runtime/RuntimeInstance.h index 349bf1dbc7b578..e50ddac18dfd40 100644 --- a/src/coreclr/nativeaot/Runtime/RuntimeInstance.h +++ b/src/coreclr/nativeaot/Runtime/RuntimeInstance.h @@ -21,7 +21,6 @@ class RuntimeInstance friend class Thread; friend void PopulateDebugHeaders(); - PTR_ThreadStore m_pThreadStore; HANDLE m_hPalInstance; // this is the HANDLE passed into DllMain public: diff --git a/src/coreclr/nativeaot/Runtime/datadescriptor/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/datadescriptor/CMakeLists.txt new file mode 100644 index 00000000000000..344dc601110f3d --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/datadescriptor/CMakeLists.txt @@ -0,0 +1,49 @@ +set(CMAKE_INCLUDE_CURRENT_DIR OFF) + +# cDAC contract descriptor for NativeAOT +# +# This uses the shared datadescriptor infrastructure from +# src/coreclr/debug/datadescriptor-shared/ to generate a +# DotNetRuntimeContractDescriptor symbol in the NativeAOT runtime. +# +# The nativeaot_runtime_includes interface library (created in the parent +# Runtime/CMakeLists.txt) provides the same include directories used by +# the regular NativeAOT Runtime build. + +include(${CLR_DIR}/clrdatadescriptors.cmake) + +add_library(nativeaot_descriptor_interface INTERFACE) +target_include_directories(nativeaot_descriptor_interface INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(nativeaot_descriptor_interface INTERFACE nativeaot_runtime_includes) +add_dependencies(nativeaot_descriptor_interface aot_eventing_headers) +generate_data_descriptors( + LIBRARY_NAME nativeaot_cdac_contract_descriptor + CONTRACT_NAME "DotNetRuntimeContractDescriptor" + INTERFACE_TARGET nativeaot_descriptor_interface + EXPORT_VISIBLE) + +# GC contract descriptors (workstation + server). +# The GC has its own data descriptor exposed as a sub-descriptor via gc_descriptor in GcDacVars. +set(GC_DESCRIPTOR_DIR "${CLR_DIR}/gc/datadescriptor") + +add_library(nativeaot_gc_wks_descriptor_interface INTERFACE) +target_include_directories(nativeaot_gc_wks_descriptor_interface INTERFACE + ${GC_DESCRIPTOR_DIR}) +target_link_libraries(nativeaot_gc_wks_descriptor_interface INTERFACE nativeaot_runtime_includes) +add_dependencies(nativeaot_gc_wks_descriptor_interface aot_eventing_headers) +generate_data_descriptors( + LIBRARY_NAME nativeaot_gc_wks_descriptor + CONTRACT_NAME "GCContractDescriptorWKS" + INTERFACE_TARGET nativeaot_gc_wks_descriptor_interface) + +add_library(nativeaot_gc_svr_descriptor_interface INTERFACE) +target_include_directories(nativeaot_gc_svr_descriptor_interface INTERFACE + ${GC_DESCRIPTOR_DIR}) +target_link_libraries(nativeaot_gc_svr_descriptor_interface INTERFACE nativeaot_runtime_includes) +add_dependencies(nativeaot_gc_svr_descriptor_interface aot_eventing_headers) +target_compile_definitions(nativeaot_gc_svr_descriptor_interface INTERFACE -DSERVER_GC) +generate_data_descriptors( + LIBRARY_NAME nativeaot_gc_svr_descriptor + CONTRACT_NAME "GCContractDescriptorSVR" + INTERFACE_TARGET nativeaot_gc_svr_descriptor_interface) diff --git a/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.h b/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.h new file mode 100644 index 00000000000000..4b5b4e1824f413 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.h @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" +#include "gcenv.h" +#include "gcheaputilities.h" +#include "gcinterface.dac.h" +#include "rhassert.h" +#include "TargetPtrs.h" +#include "PalLimitedContext.h" +#include "Pal.h" +#include "holder.h" +#include "RuntimeInstance.h" +#include "regdisplay.h" +#include "StackFrameIterator.h" +#include "thread.h" +#include "threadstore.h" + +#include +#include + +GPTR_DECL(MethodTable, g_pFreeObjectEEType); +GPTR_DECL(StressLog, g_pStressLog); + +// ILC emits a ContractDescriptor named "DotNetManagedContractDescriptor" with +// managed type layouts. We take its address so datadescriptor.inc can reference +// it as a sub-descriptor via CDAC_GLOBAL_SUB_DESCRIPTOR. +struct ContractDescriptor; +extern "C" ContractDescriptor DotNetManagedContractDescriptor; +static const void* g_pManagedContractDescriptor = &DotNetManagedContractDescriptor; diff --git a/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.inc b/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.inc new file mode 100644 index 00000000000000..a3aaf5376bfddf --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/datadescriptor/datadescriptor.inc @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// No include guards. This file is included multiple times. +// +// NativeAOT data descriptor declarations for the cDAC contract system. +// This file defines the types, fields, and globals that diagnostic tools +// need to inspect NativeAOT runtime state. +// +// When modifying this file (adding/removing types, fields, or globals), you must also: +// 1. Update the corresponding contract doc in docs/design/datacontracts/.md +// 2. Update the managed data class in src/native/managed/cdac/.../Data/.cs +// 3. Update the contract implementation in src/native/managed/cdac/.../Contracts/.cs +// 4. Update the mock descriptors and tests in src/native/managed/cdac/tests/. + +CDAC_BASELINE("empty") +CDAC_TYPES_BEGIN() + +// ======================== +// Thread and ThreadStore +// ======================== + +CDAC_TYPE_BEGIN(Thread) +CDAC_TYPE_INDETERMINATE(Thread) +CDAC_TYPE_FIELD(Thread, T_UINT64, OSId, offsetof(RuntimeThreadLocals, m_threadId)) +CDAC_TYPE_FIELD(Thread, T_UINT32, State, offsetof(RuntimeThreadLocals, m_ThreadStateFlags)) +CDAC_TYPE_FIELD(Thread, T_POINTER, LinkNext, offsetof(RuntimeThreadLocals, m_pNext)) +CDAC_TYPE_FIELD(Thread, T_POINTER, ExceptionTracker, offsetof(RuntimeThreadLocals, m_pExInfoStackHead)) +CDAC_TYPE_FIELD(Thread, T_POINTER, CachedStackBase, offsetof(RuntimeThreadLocals, m_pStackHigh)) +CDAC_TYPE_FIELD(Thread, T_POINTER, CachedStackLimit, offsetof(RuntimeThreadLocals, m_pStackLow)) +// Thread inherits from RuntimeThreadLocals at offset 0, so EEAllocContext is directly on Thread +CDAC_TYPE_FIELD(Thread, TYPE(EEAllocContext), AllocContext, offsetof(RuntimeThreadLocals, m_eeAllocContext)) +CDAC_TYPE_FIELD(Thread, T_POINTER, TransitionFrame, offsetof(RuntimeThreadLocals, m_pTransitionFrame)) +CDAC_TYPE_END(Thread) + +CDAC_TYPE_BEGIN(ThreadStore) +CDAC_TYPE_INDETERMINATE(ThreadStore) +CDAC_TYPE_FIELD(ThreadStore, T_POINTER, FirstThreadLink, cdac_data::FirstThreadLink) +CDAC_TYPE_END(ThreadStore) + +// ======================== +// Allocation Context +// ======================== + +CDAC_TYPE_BEGIN(EEAllocContext) +CDAC_TYPE_INDETERMINATE(EEAllocContext) +CDAC_TYPE_FIELD(EEAllocContext, TYPE(GCAllocContext), GCAllocationContext, offsetof(ee_alloc_context, m_rgbAllocContextBuffer)) +CDAC_TYPE_END(EEAllocContext) + +CDAC_TYPE_BEGIN(GCAllocContext) +CDAC_TYPE_INDETERMINATE(GCAllocContext) +CDAC_TYPE_FIELD(GCAllocContext, T_POINTER, Pointer, offsetof(gc_alloc_context, alloc_ptr)) +CDAC_TYPE_FIELD(GCAllocContext, T_POINTER, Limit, offsetof(gc_alloc_context, alloc_limit)) +CDAC_TYPE_FIELD(GCAllocContext, T_INT64, AllocBytes, offsetof(gc_alloc_context, alloc_bytes)) +CDAC_TYPE_FIELD(GCAllocContext, T_INT64, AllocBytesLoh, offsetof(gc_alloc_context, alloc_bytes_uoh)) +CDAC_TYPE_END(GCAllocContext) + +// ======================== +// MethodTable (EEType) +// ======================== + +CDAC_TYPE_BEGIN(MethodTable) +CDAC_TYPE_INDETERMINATE(MethodTable) +CDAC_TYPE_FIELD(MethodTable, T_UINT32, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(MethodTable, T_UINT32, BaseSize, cdac_data::BaseSize) +CDAC_TYPE_FIELD(MethodTable, T_POINTER, RelatedType, cdac_data::RelatedType) +CDAC_TYPE_FIELD(MethodTable, T_UINT16, NumVtableSlots, cdac_data::NumVtableSlots) +CDAC_TYPE_FIELD(MethodTable, T_UINT16, NumInterfaces, cdac_data::NumInterfaces) +CDAC_TYPE_FIELD(MethodTable, T_UINT32, HashCode, cdac_data::HashCode) +CDAC_TYPE_FIELD(MethodTable, T_POINTER, VTable, cdac_data::VTable) +CDAC_TYPE_END(MethodTable) + +// ======================== +// Exception Info +// ======================== + +CDAC_TYPE_BEGIN(ExInfo) +CDAC_TYPE_INDETERMINATE(ExInfo) +CDAC_TYPE_FIELD(ExInfo, T_POINTER, PreviousNestedInfo, offsetof(ExInfo, m_pPrevExInfo)) +CDAC_TYPE_FIELD(ExInfo, T_POINTER, ThrownObject, offsetof(ExInfo, m_exception)) +CDAC_TYPE_END(ExInfo) + +// ======================== +// StressLog +// ======================== + +CDAC_TYPE_BEGIN(StressLog) +CDAC_TYPE_SIZE(sizeof(StressLog)) +CDAC_TYPE_FIELD(StressLog, T_UINT32, LoggedFacilities, offsetof(StressLog, facilitiesToLog)) +CDAC_TYPE_FIELD(StressLog, T_UINT32, Level, offsetof(StressLog, levelToLog)) +CDAC_TYPE_FIELD(StressLog, T_UINT32, MaxSizePerThread, offsetof(StressLog, MaxSizePerThread)) +CDAC_TYPE_FIELD(StressLog, T_UINT32, MaxSizeTotal, offsetof(StressLog, MaxSizeTotal)) +CDAC_TYPE_FIELD(StressLog, T_INT32, TotalChunks, offsetof(StressLog, totalChunk)) +CDAC_TYPE_FIELD(StressLog, T_POINTER, Logs, offsetof(StressLog, logs)) +CDAC_TYPE_FIELD(StressLog, T_UINT64, TickFrequency, offsetof(StressLog, tickFrequency)) +CDAC_TYPE_FIELD(StressLog, T_UINT64, StartTimestamp, offsetof(StressLog, startTimeStamp)) +CDAC_TYPE_END(StressLog) + +CDAC_TYPE_BEGIN(ThreadStressLog) +CDAC_TYPE_INDETERMINATE(ThreadStressLog) +CDAC_TYPE_FIELD(ThreadStressLog, T_POINTER, Next, cdac_data::Next) +CDAC_TYPE_FIELD(ThreadStressLog, T_UINT64, ThreadId, cdac_data::ThreadId) +CDAC_TYPE_FIELD(ThreadStressLog, T_UINT8, WriteHasWrapped, cdac_data::WriteHasWrapped) +CDAC_TYPE_FIELD(ThreadStressLog, T_POINTER, CurrentPtr, cdac_data::CurrentPtr) +CDAC_TYPE_FIELD(ThreadStressLog, T_POINTER, ChunkListHead, cdac_data::ChunkListHead) +CDAC_TYPE_FIELD(ThreadStressLog, T_POINTER, ChunkListTail, cdac_data::ChunkListTail) +CDAC_TYPE_FIELD(ThreadStressLog, T_POINTER, CurrentWriteChunk, cdac_data::CurrentWriteChunk) +CDAC_TYPE_END(ThreadStressLog) + +CDAC_TYPE_BEGIN(StressLogChunk) +CDAC_TYPE_SIZE(sizeof(StressLogChunk)) +CDAC_TYPE_FIELD(StressLogChunk, T_POINTER, Prev, offsetof(StressLogChunk, prev)) +CDAC_TYPE_FIELD(StressLogChunk, T_POINTER, Next, offsetof(StressLogChunk, next)) +CDAC_TYPE_FIELD(StressLogChunk, T_ARRAY(T_UINT8), Buf, offsetof(StressLogChunk, buf)) +CDAC_TYPE_FIELD(StressLogChunk, T_UINT32, Sig1, offsetof(StressLogChunk, dwSig1)) +CDAC_TYPE_FIELD(StressLogChunk, T_UINT32, Sig2, offsetof(StressLogChunk, dwSig2)) +CDAC_TYPE_END(StressLogChunk) + +CDAC_TYPE_BEGIN(StressMsgHeader) +CDAC_TYPE_SIZE(sizeof(StressMsg)) +CDAC_TYPE_END(StressMsgHeader) + +CDAC_TYPE_BEGIN(StressMsg) +CDAC_TYPE_INDETERMINATE(StressMsg) +CDAC_TYPE_FIELD(StressMsg, TYPE(StressMsgHeader), Header, 0) +CDAC_TYPE_FIELD(StressMsg, T_POINTER, Args, offsetof(StressMsg, args)) +CDAC_TYPE_END(StressMsg) + +CDAC_TYPES_END() + +// ======================== +// Globals +// ======================== + +CDAC_GLOBALS_BEGIN() + +CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) + +CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &g_pFreeObjectEEType) + +CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address) +CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address) + +// Thread state flag constants +CDAC_GLOBAL(ThreadStateFlagAttached, T_UINT32, 0x00000001) +CDAC_GLOBAL(ThreadStateFlagDetached, T_UINT32, 0x00000002) + +// Object contract globals +#ifdef TARGET_64BIT +CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 7) +#else +CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 3) +#endif + +// StressLog globals (no module table in NativeAOT) +CDAC_GLOBAL(StressLogEnabled, T_UINT8, 1) +CDAC_GLOBAL_POINTER(StressLog, &g_pStressLog) +CDAC_GLOBAL(StressLogHasModuleTable, T_UINT8, 0) +CDAC_GLOBAL(StressLogChunkSize, T_UINT32, STRESSLOG_CHUNK_SIZE) +CDAC_GLOBAL(StressLogValidChunkSig, T_UINT32, 0xCFCFCFCF) +CDAC_GLOBAL(StressLogMaxMessageSize, T_UINT64, (uint64_t)StressMsg::maxMsgSize) + +// Contracts: declare which contracts this runtime supports +CDAC_GLOBAL_CONTRACT(Thread, n1) +CDAC_GLOBAL_CONTRACT(Exception, c1) +CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, n1) +CDAC_GLOBAL_CONTRACT(StressLog, c2) + +// Managed type sub-descriptor: ILC emits a ContractDescriptor with managed type layouts +// that the cDAC reader merges as a sub-descriptor. This provides field offsets for managed +// types (e.g., ConditionalWeakTable internals, IdDispenser) that are not exposed through +// native C++ data descriptors. +CDAC_GLOBAL_SUB_DESCRIPTOR(ManagedTypes, &g_pManagedContractDescriptor) + +// GC sub-descriptor: the GC populates gc_descriptor during GC_Initialize. +// It is important for subdescriptor pointers to be the last pointers. +CDAC_GLOBAL_SUB_DESCRIPTOR(GC, &(g_gc_dac_vars.gc_descriptor)) + +CDAC_GLOBALS_END() diff --git a/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp index 2678b12c1aea0b..edafc599814e51 100644 --- a/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp +++ b/src/coreclr/nativeaot/Runtime/gcheaputilities.cpp @@ -7,6 +7,7 @@ #include "gchandleutilities.h" #include "gceventstatus.h" +#include "gcinterface.h" // This is the global GC heap, maintained by the VM. GPTR_IMPL(IGCHeap, g_pGCHeap); @@ -71,6 +72,8 @@ HRESULT GCHeapUtilities::InitializeDefaultGC() IGCHeap* heap; IGCHandleManager* manager; + g_gc_dac_vars.major_version_number = GC_INTERFACE_MAJOR_VERSION; + g_gc_dac_vars.minor_version_number = GC_INTERFACE_MINOR_VERSION; HRESULT initResult = GC_Initialize(nullptr, &heap, &manager, &g_gc_dac_vars); if (initResult == S_OK) { diff --git a/src/coreclr/nativeaot/Runtime/inc/MethodTable.h b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h index 89ff445f6174d9..8ff6c556404cf2 100644 --- a/src/coreclr/nativeaot/Runtime/inc/MethodTable.h +++ b/src/coreclr/nativeaot/Runtime/inc/MethodTable.h @@ -12,6 +12,8 @@ class MethodTable; class TypeManager; struct TypeManagerHandle; +#include "cdacdata.h" + //------------------------------------------------------------------------------------------------- // The subset of TypeFlags that NativeAOT knows about at runtime // This should match the TypeFlags enum in the managed type system. @@ -86,6 +88,7 @@ class MethodTable { friend class AsmOffsets; friend void PopulateDebugHeaders(); + friend struct ::cdac_data; private: struct RelatedTypeUnion @@ -346,4 +349,15 @@ class MethodTable UInt32_BOOL SanityCheck() { return Validate(); } }; +template<> struct cdac_data +{ + static constexpr size_t Flags = offsetof(MethodTable, m_uFlags); + static constexpr size_t BaseSize = offsetof(MethodTable, m_uBaseSize); + static constexpr size_t RelatedType = offsetof(MethodTable, m_RelatedType); + static constexpr size_t NumVtableSlots = offsetof(MethodTable, m_usNumVtableSlots); + static constexpr size_t NumInterfaces = offsetof(MethodTable, m_usNumInterfaces); + static constexpr size_t HashCode = offsetof(MethodTable, m_uHashCode); + static constexpr size_t VTable = offsetof(MethodTable, m_VTable); +}; + #pragma warning(pop) diff --git a/src/coreclr/nativeaot/Runtime/inc/cdacdata.h b/src/coreclr/nativeaot/Runtime/inc/cdacdata.h new file mode 100644 index 00000000000000..ffbf4431b381c7 --- /dev/null +++ b/src/coreclr/nativeaot/Runtime/inc/cdacdata.h @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "../../../inc/cdacdata.h" diff --git a/src/coreclr/nativeaot/Runtime/inc/stressLog.h b/src/coreclr/nativeaot/Runtime/inc/stressLog.h index 17bf5fbfee1b20..888edc33961057 100644 --- a/src/coreclr/nativeaot/Runtime/inc/stressLog.h +++ b/src/coreclr/nativeaot/Runtime/inc/stressLog.h @@ -47,6 +47,8 @@ #if defined(STRESS_LOG) +#include "cdacdata.h" + // // Logging levels and facilities // @@ -532,6 +534,7 @@ class ThreadStressLog { PTR_Thread pThread; // thread associated with these stress logs StressMsg * origCurPtr; // this holds the original curPtr before we start the dump + template friend struct ::cdac_data; friend void PopulateDebugHeaders(); friend class StressLog; @@ -793,4 +796,17 @@ inline StressMsg* ThreadStressLog::AdvWritePastBoundary(int cArgs) { #endif // !STRESS_LOG || DACCESS_COMPILE #endif // !__GCENV_BASE_INCLUDED__ +#if defined(STRESS_LOG) +template<> struct cdac_data +{ + static constexpr size_t Next = offsetof(ThreadStressLog, next); + static constexpr size_t ThreadId = offsetof(ThreadStressLog, threadId); + static constexpr size_t WriteHasWrapped = offsetof(ThreadStressLog, writeHasWrapped); + static constexpr size_t CurrentPtr = offsetof(ThreadStressLog, curPtr); + static constexpr size_t ChunkListHead = offsetof(ThreadStressLog, chunkListHead); + static constexpr size_t ChunkListTail = offsetof(ThreadStressLog, chunkListTail); + static constexpr size_t CurrentWriteChunk = offsetof(ThreadStressLog, curWriteChunk); +}; +#endif // STRESS_LOG + #endif // StressLog_h diff --git a/src/coreclr/nativeaot/Runtime/threadstore.cpp b/src/coreclr/nativeaot/Runtime/threadstore.cpp index 0439f0c76058ab..c87207a562ba62 100644 --- a/src/coreclr/nativeaot/Runtime/threadstore.cpp +++ b/src/coreclr/nativeaot/Runtime/threadstore.cpp @@ -30,6 +30,8 @@ volatile uint32_t RhpTrapThreads = (uint32_t)TrapThreadsFlags::None; GVAL_IMPL_INIT(PTR_Thread, RhpSuspendingThread, 0); +SPTR_IMPL(ThreadStore, ThreadStore, s_pThreadStore); + ThreadStore * GetThreadStore() { return GetRuntimeInstance()->GetThreadStore(); diff --git a/src/coreclr/nativeaot/Runtime/threadstore.h b/src/coreclr/nativeaot/Runtime/threadstore.h index d2347f9a631ffa..5f855dc5b18abd 100644 --- a/src/coreclr/nativeaot/Runtime/threadstore.h +++ b/src/coreclr/nativeaot/Runtime/threadstore.h @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "Crst.h" +#include "cdacdata.h" class Thread; class CLREventStatic; @@ -19,7 +20,9 @@ extern "C" void PopulateDebugHeaders(); class ThreadStore { + friend class RuntimeInstance; friend void PopulateDebugHeaders(); + friend struct ::cdac_data; SList m_ThreadList; PTR_RuntimeInstance m_pRuntimeInstance; @@ -29,6 +32,7 @@ class ThreadStore ThreadStore(); public: + SPTR_DECL(ThreadStore, s_pThreadStore); void LockThreadStore(); void UnlockThreadStore(); @@ -68,6 +72,11 @@ class ThreadStore }; typedef DPTR(ThreadStore) PTR_ThreadStore; +template<> struct cdac_data +{ + static constexpr size_t FirstThreadLink = offsetof(ThreadStore, m_ThreadList); +}; + ThreadStore * GetThreadStore(); #define FOREACH_THREAD(p_thread_name) \ diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index d120f10da497ac..0f808ede316353 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -195,6 +195,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DataContractAttribute.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DataContractAttribute.cs new file mode 100644 index 00000000000000..062a9d311c5972 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Diagnostics/DataContractAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics +{ + /// + /// When applied to a type, indicates that ILC should include its field layout in the + /// managed cDAC data descriptor so diagnostic tools can inspect instances without + /// runtime metadata or symbols. When applied to a field of such a type, indicates ILC should + /// include that field's offset in the descriptor. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field, Inherited = false)] + internal sealed class DataContractAttribute : Attribute + { + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 3f7aa4ffc429df..5bd5dcdc717cc9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -13,6 +13,7 @@ namespace System.Threading { + [DataContract] public sealed partial class Thread { // Extra bits used in _threadState @@ -29,7 +30,9 @@ public sealed partial class Thread private volatile int _threadState = (int)ThreadState.Unstarted; private ThreadPriority _priority; + [DataContract] private ManagedThreadId _managedThreadId; + [DataContract] private string? _name; private StartHelper? _startHelper; private Exception? _startException; diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index 5d085be67b1061..20dd74882a1c2d 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -3,7 +3,7 @@ <_BuildNativeTargetOS>$(TargetOS) <_BuildNativeTargetOS Condition="'$(TargetsLinuxBionic)' == 'true'">linux-bionic - true + true GetPgoDataPackagePath AcquireEmscriptenSdk;$(BuildRuntimeDependsOnTargets);GenerateEmccExports diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ManagedDataDescriptorNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ManagedDataDescriptorNode.cs new file mode 100644 index 00000000000000..7041768e63d325 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ManagedDataDescriptorNode.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text.Json; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Emits a ContractDescriptor for managed type layouts that the cDAC reader + /// can consume as a sub-descriptor. ILC knows managed type layouts at compile time, + /// so it can emit field offsets that would otherwise require runtime metadata resolution. + /// + /// Types are discovered by scanning MetadataManager.GetTypesWithEETypes() for types + /// annotated with [DataContract], ensuring only types that actually have a MethodTable + /// in the binary are included. + /// + public class ManagedDataDescriptorNode : ObjectNode, ISymbolDefinitionNode + { + private const string DataContractAttributeNamespace = "System.Diagnostics"; + private const string DataContractAttributeName = "DataContractAttribute"; + + public const string SymbolName = "DotNetManagedContractDescriptor"; + + public override ObjectNodeSection GetSection(NodeFactory factory) => + factory.Target.IsWindows ? ObjectNodeSection.ReadOnlyDataSection : ObjectNodeSection.DataSection; + + public override bool StaticDependenciesAreComputed => true; + public override bool IsShareable => false; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.NodeMangler.ExternVariable(new Utf8String(SymbolName))); + } + + public int Offset => 0; + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); + + byte[] jsonBytes = BuildJsonDescriptor(factory); + + // Header layout: magic(8) + flags(4) + desc_size(4) + desc_ptr(ptr) + pointer_data_count(4) + pad(4) + pointer_data(ptr) + int headerSize = 8 + 4 + 4 + factory.Target.PointerSize + 4 + 4 + factory.Target.PointerSize; + + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.RequireInitialPointerAlignment(); + builder.AddSymbol(this); + + // uint64_t magic + builder.EmitLong(0x0043414443434e44L); // "DNCCDAC\0" + + // uint32_t flags (bit 0 must be set; bit 1 indicates 32-bit pointers) + uint flags = (uint)(0x01 | (factory.Target.PointerSize == 4 ? 0x02 : 0x00)); + builder.EmitUInt(flags); + + // uint32_t descriptor_size + builder.EmitUInt((uint)jsonBytes.Length); + + // char* descriptor — points to inline JSON after the header + builder.EmitPointerReloc(this, headerSize); + + // uint32_t pointer_data_count = 0 + builder.EmitUInt(0); + + // uint32_t pad0 + builder.EmitUInt(0); + + // void** pointer_data = null + builder.EmitZeroPointer(); + + // Emit JSON bytes inline, null-terminated + builder.EmitBytes(jsonBytes); + builder.EmitByte(0); + + return builder.ToObjectData(); + } + + /// + /// Build the JSON descriptor using the compact format expected by the cDAC reader's + /// ContractDescriptorParser. Types are objects with an optional "!" size sigil and + /// field-name properties mapped to their offsets. + /// + private static byte[] BuildJsonDescriptor(NodeFactory factory) + { + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) + { + writer.WriteStartObject(); + writer.WriteNumber("version", 0); + writer.WriteString("baseline", "empty"); + + writer.WriteStartObject("types"); + foreach (TypeDesc type in factory.MetadataManager.GetTypesWithEETypes()) + { + if (type is not EcmaType ecmaType) + continue; + + if (!ecmaType.HasCustomAttribute(DataContractAttributeNamespace, DataContractAttributeName)) + continue; + + WriteType(writer, ecmaType); + } + writer.WriteEndObject(); + + writer.WriteStartObject("globals"); + writer.WriteEndObject(); + + writer.WriteStartObject("contracts"); + writer.WriteEndObject(); + + writer.WriteEndObject(); + } + + return stream.ToArray(); + } + + private static void WriteType(Utf8JsonWriter writer, EcmaType type) + { + writer.WriteStartObject(GetFullTypeName(type)); + + if (type.IsValueType) + { + writer.WriteNumber("!", type.InstanceFieldSize.AsInt); + } + + foreach (FieldDesc field in type.GetFields()) + { + if (field.IsStatic || field is not EcmaField ecmaField) + continue; + + if (!ecmaField.HasCustomAttribute(DataContractAttributeNamespace, DataContractAttributeName)) + continue; + + writer.WriteNumber(field.GetName(), field.Offset.AsInt); + } + + writer.WriteEndObject(); + } + + /// + /// Returns a fully-qualified type name for cDAC descriptors + /// (e.g., "System.Threading.Thread" or "System.Foo.Outer+Inner"). + /// + private static string GetFullTypeName(MetadataType type) + { + if (type.ContainingType is not null) + return $"{GetFullTypeName(type.ContainingType)}+{type.GetName()}"; + + string ns = type.GetNamespace(); + string name = type.GetName(); + + if (string.IsNullOrEmpty(ns)) + return name; + + return $"{ns}.{name}"; + } + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => 0x4d444e01; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedDataDescriptorProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedDataDescriptorProvider.cs new file mode 100644 index 00000000000000..734b9d1ad7faf8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedDataDescriptorProvider.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler +{ + /// + /// Compilation root provider that adds the managed cDAC data descriptor node. + /// The node discovers [DataContract]-annotated types from MetadataManager.GetTypesWithEETypes() + /// during object data emission, ensuring only types with MethodTables are included. + /// + public class ManagedDataDescriptorProvider : ICompilationRootProvider + { + void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider) + { + var descriptorNode = new ManagedDataDescriptorNode(); + rootProvider.AddCompilationRoot(descriptorNode, "Managed type descriptors for cDAC"); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index a0e5f77c7c533b..791eb295455999 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -596,6 +596,7 @@ + @@ -689,6 +690,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 9786ffc489b473..5bb46ef0fae168 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -263,6 +263,7 @@ public int Run() compilationRoots.Add(new RuntimeConfigurationRootProvider(settingsBlobName, runtimeOptions)); compilationRoots.Add(new RuntimeConfigurationRootProvider(knobsBlobName, runtimeKnobs)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + compilationRoots.Add(new ManagedDataDescriptorProvider()); if (SplitExeInitialization) { compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext), generateLibraryAndModuleInitializers: false)); @@ -274,6 +275,7 @@ public int Run() compilationRoots.Add(new RuntimeConfigurationRootProvider(settingsBlobName, runtimeOptions)); compilationRoots.Add(new RuntimeConfigurationRootProvider(knobsBlobName, runtimeKnobs)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); + compilationRoots.Add(new ManagedDataDescriptorProvider()); if (SplitExeInitialization) { compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext)));