diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 50e3a954ba6f4e..6abe3e33cd794c 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -152,10 +152,10 @@ public readonly struct GCMemoryRegionData Data descriptors used: | Data Descriptor Name | Field | Source | Meaning | | --- | --- | --- | --- | -| `GCHeap` | MarkArray | GC | Pointer to the heap's MarkArray (in sever builds) | -| `GCHeap` | NextSweepObj | GC | Pointer to the heap's NextSweepObj (in sever builds) | -| `GCHeap` | BackgroundMinSavedAddr | GC | Heap's background saved lowest address (in sever builds) | -| `GCHeap` | BackgroundMaxSavedAddr | GC | Heap's background saved highest address (in sever builds) | +| `GCHeap` | MarkArray | GC | Pointer to the heap's MarkArray (only in server builds with background GC) | +| `GCHeap` | NextSweepObj | GC | Pointer to the heap's NextSweepObj (only in server builds with background GC) | +| `GCHeap` | BackgroundMinSavedAddr | GC | Heap's background saved lowest address (only in server builds with background GC) | +| `GCHeap` | BackgroundMaxSavedAddr | GC | Heap's background saved highest address (only in server builds with background GC) | | `GCHeap` | AllocAllocated | GC | Heap's highest address allocated by Alloc (in sever builds) | | `GCHeap` | EphemeralHeapSegment | GC | Pointer to the heap's ephemeral heap segment (in sever builds) | | `GCHeap` | CardTable | GC | Pointer to the heap's bookkeeping GC data structure (in sever builds) | @@ -229,10 +229,10 @@ Global variables used: | `CompactReasonsLength` | uint | GC | The number of elements in the `CompactReasons` array | | `ExpandMechanismsLength` | uint | GC | The number of elements in the `ExpandMechanisms` array | | `InterestingMechanismBitsLength` | uint | GC | The number of elements in the `InterestingMechanismBits` array | -| `GCHeapMarkArray` | TargetPointer | GC | Pointer to the static heap's MarkArray (in workstation builds) | -| `GCHeapNextSweepObj` | TargetPointer | GC | Pointer to the static heap's NextSweepObj (in workstation builds) | -| `GCHeapBackgroundMinSavedAddr` | TargetPointer | GC | Background saved lowest address (in workstation builds) | -| `GCHeapBackgroundMaxSavedAddr` | TargetPointer | GC | Background saved highest address (in workstation builds) | +| `GCHeapMarkArray` | TargetPointer | GC | Pointer to the static heap's MarkArray (in workstation builds with background GC) | +| `GCHeapNextSweepObj` | TargetPointer | GC | Pointer to the static heap's NextSweepObj (in workstation builds with background GC) | +| `GCHeapBackgroundMinSavedAddr` | TargetPointer | GC | Background saved lowest address (in workstation builds with background GC) | +| `GCHeapBackgroundMaxSavedAddr` | TargetPointer | GC | Background saved highest address (in workstation builds with background GC) | | `GCHeapAllocAllocated` | TargetPointer | GC | Highest address allocated by Alloc (in workstation builds) | | `GCHeapEphemeralHeapSegment` | TargetPointer | GC | Pointer to an ephemeral heap segment (in workstation builds) | | `GCHeapCardTable` | TargetPointer | GC | Pointer to the static heap's bookkeeping GC data structure (in workstation builds) | @@ -454,11 +454,39 @@ GCHeapData IGC.GetHeapData() GCHeapData data; - // Read fields directly from globals - data.MarkArray = target.ReadPointer(target.ReadGlobalPointer("GCHeapMarkArray")); - data.NextSweepObj = target.ReadPointer(target.ReadGlobalPointer("GCHeapNextSweepObj")); - data.BackgroundMinSavedAddr = target.ReadPointer(target.ReadGlobalPointer("GCHeapBackgroundMinSavedAddr")); - data.BackgroundMaxSavedAddr = target.ReadPointer(target.ReadGlobalPointer("GCHeapBackgroundMaxSavedAddr")); + // Read background GC globals - these are absent when background GC is disabled (e.g., on WebAssembly). + if (target.TryReadGlobalPointer("GCHeapMarkArray", out TargetPointer? markArrayPtr)) + { + data.MarkArray = target.ReadPointer(markArrayPtr.Value); + } + else + { + data.MarkArray = 0; + } + if (target.TryReadGlobalPointer("GCHeapNextSweepObj", out TargetPointer? nextSweepObjPtr)) + { + data.NextSweepObj = target.ReadPointer(nextSweepObjPtr.Value); + } + else + { + data.NextSweepObj = 0; + } + if (target.TryReadGlobalPointer("GCHeapBackgroundMinSavedAddr", out TargetPointer? bgMinPtr)) + { + data.BackgroundMinSavedAddr = target.ReadPointer(bgMinPtr.Value); + } + else + { + data.BackgroundMinSavedAddr = 0; + } + if (target.TryReadGlobalPointer("GCHeapBackgroundMaxSavedAddr", out TargetPointer? bgMaxPtr)) + { + data.BackgroundMaxSavedAddr = target.ReadPointer(bgMaxPtr.Value); + } + else + { + data.BackgroundMaxSavedAddr = 0; + } data.AllocAllocated = target.ReadPointer(target.ReadGlobalPointer("GCHeapAllocAllocated")); data.EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer("GCHeapEphemeralHeapSegment")); data.CardTable = target.ReadPointer(target.ReadGlobalPointer("GCHeapCardTable")); @@ -521,11 +549,41 @@ GCHeapData IGC.GetHeapData(TargetPointer heapAddress) GCHeapData data; - // Read fields directly from heap - data.MarkArray = target.ReadPointer(heapAddress + /* GCHeap::MarkArray offset */); - data.NextSweepObj = target.ReadPointer(heapAddress + /* GCHeap::NextSweepObj offset */); - data.BackgroundMinSavedAddr = target.ReadPointer(heapAddress + /* GCHeap::BackgroundMinSavedAddr offset */); - data.BackgroundMaxSavedAddr = target.ReadPointer(heapAddress + /* GCHeap::BackgroundMaxSavedAddr offset */); + // Read background GC heap fields - these fields are absent when background GC is disabled (e.g., on WebAssembly). + // Check whether the field exists in the type layout before reading; default to 0 if not present. + Target.TypeInfo gcHeapType = target.GetTypeInfo(DataType.GCHeap); + if (gcHeapType.Fields.ContainsKey("MarkArray")) + { + data.MarkArray = target.ReadPointer(heapAddress + /* GCHeap::MarkArray offset */); + } + else + { + data.MarkArray = 0; + } + if (gcHeapType.Fields.ContainsKey("NextSweepObj")) + { + data.NextSweepObj = target.ReadPointer(heapAddress + /* GCHeap::NextSweepObj offset */); + } + else + { + data.NextSweepObj = 0; + } + if (gcHeapType.Fields.ContainsKey("BackgroundMinSavedAddr")) + { + data.BackgroundMinSavedAddr = target.ReadPointer(heapAddress + /* GCHeap::BackgroundMinSavedAddr offset */); + } + else + { + data.BackgroundMinSavedAddr = 0; + } + if (gcHeapType.Fields.ContainsKey("BackgroundMaxSavedAddr")) + { + data.BackgroundMaxSavedAddr = target.ReadPointer(heapAddress + /* GCHeap::BackgroundMaxSavedAddr offset */); + } + else + { + data.BackgroundMaxSavedAddr = 0; + } data.AllocAllocated = target.ReadPointer(heapAddress + /* GCHeap::AllocAllocated offset */); data.EphemeralHeapSegment = target.ReadPointer(heapAddress + /* GCHeap::EphemeralHeapSegment offset */); data.CardTable = target.ReadPointer(heapAddress + /* GCHeap::CardTable offset */); diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 60b79850f53ffd..6ad026ec0254a5 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -173,10 +173,10 @@ endif (CLR_CMAKE_HOST_UNIX AND CLR_CMAKE_TARGET_UNIX) if (FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) add_definitions(-DFEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) endif(FEATURE_ENABLE_NO_ADDRESS_SPACE_RANDOMIZATION) -if (NOT CLR_CMAKE_HOST_ANDROID) +if (NOT CLR_CMAKE_HOST_ANDROID AND NOT CLR_CMAKE_TARGET_ARCH_WASM AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS) set(FEATURE_SVR_GC 1) add_definitions(-DFEATURE_SVR_GC) -endif(NOT CLR_CMAKE_HOST_ANDROID) +endif(NOT CLR_CMAKE_HOST_ANDROID AND NOT CLR_CMAKE_TARGET_ARCH_WASM AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS) add_definitions(-DFEATURE_SYMDIFF) if (FEATURE_TIERED_COMPILATION) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index d9589fedb0e056..ad38175f80bcd5 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -52,19 +52,21 @@ struct cdac_data GC_HEAP_FIELD(OomData, oom_info) /* For use in GCHeapDetails APIs */ +#ifdef BACKGROUND_GC GC_HEAP_FIELD(MarkArray, mark_array) GC_HEAP_FIELD(NextSweepObj, next_sweep_obj) GC_HEAP_FIELD(BackgroundMinSavedAddr, background_saved_lowest_address) GC_HEAP_FIELD(BackgroundMaxSavedAddr, background_saved_highest_address) +#endif // BACKGROUND_GC GC_HEAP_FIELD(AllocAllocated, alloc_allocated) GC_HEAP_FIELD(EphemeralHeapSegment, ephemeral_heap_segment) GC_HEAP_FIELD(CardTable, card_table) GC_HEAP_FIELD(FinalizeQueue, finalize_queue) GC_HEAP_FIELD(GenerationTable, generation_table) -#ifndef USE_REGIONS +#if !defined(USE_REGIONS) && defined(BACKGROUND_GC) GC_HEAP_FIELD(SavedSweepEphemeralSeg, saved_sweep_ephemeral_seg) GC_HEAP_FIELD(SavedSweepEphemeralStart, saved_sweep_ephemeral_start) -#endif // !USE_REGIONS +#endif // !USE_REGIONS && BACKGROUND_GC /* For use in GCHeapAnalyzeData APIs */ GC_HEAP_FIELD(InternalRootArray, internal_root_array) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index 49243687f13f4b..4377522f869d98 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -13,10 +13,12 @@ CDAC_TYPES_BEGIN() #ifdef SERVER_GC CDAC_TYPE_BEGIN(GCHeap) CDAC_TYPE_INDETERMINATE(GCHeap) +#ifdef BACKGROUND_GC CDAC_TYPE_FIELD(GCHeap, T_POINTER, MarkArray, cdac_data::MarkArray) CDAC_TYPE_FIELD(GCHeap, T_POINTER, NextSweepObj, cdac_data::NextSweepObj) CDAC_TYPE_FIELD(GCHeap, T_POINTER, BackgroundMinSavedAddr, cdac_data::BackgroundMinSavedAddr) CDAC_TYPE_FIELD(GCHeap, T_POINTER, BackgroundMaxSavedAddr, cdac_data::BackgroundMaxSavedAddr) +#endif // BACKGROUND_GC CDAC_TYPE_FIELD(GCHeap, T_POINTER, AllocAllocated, cdac_data::AllocAllocated) CDAC_TYPE_FIELD(GCHeap, T_POINTER, EphemeralHeapSegment, cdac_data::EphemeralHeapSegment) CDAC_TYPE_FIELD(GCHeap, T_POINTER, CardTable, cdac_data::CardTable) @@ -152,19 +154,21 @@ CDAC_GLOBAL(CountFreeRegionKinds, T_UINT32, (uint32_t)FREE_REGION_KINDS) #ifndef SERVER_GC +#ifdef BACKGROUND_GC CDAC_GLOBAL_POINTER(GCHeapMarkArray, cdac_data::MarkArray) CDAC_GLOBAL_POINTER(GCHeapNextSweepObj, cdac_data::NextSweepObj) CDAC_GLOBAL_POINTER(GCHeapBackgroundMinSavedAddr, cdac_data::BackgroundMinSavedAddr) CDAC_GLOBAL_POINTER(GCHeapBackgroundMaxSavedAddr, cdac_data::BackgroundMaxSavedAddr) +#endif // BACKGROUND_GC CDAC_GLOBAL_POINTER(GCHeapAllocAllocated, cdac_data::AllocAllocated) CDAC_GLOBAL_POINTER(GCHeapEphemeralHeapSegment, cdac_data::EphemeralHeapSegment) CDAC_GLOBAL_POINTER(GCHeapCardTable, cdac_data::CardTable) CDAC_GLOBAL_POINTER(GCHeapFinalizeQueue, cdac_data::FinalizeQueue) CDAC_GLOBAL_POINTER(GCHeapGenerationTable, cdac_data::GenerationTable) -#ifndef USE_REGIONS +#if !defined(USE_REGIONS) && defined(BACKGROUND_GC) CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralSeg, cdac_data::SavedSweepEphemeralSeg) CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralStart, cdac_data::SavedSweepEphemeralStart) -#endif // !USE_REGIONS +#endif // !USE_REGIONS && BACKGROUND_GC CDAC_GLOBAL_POINTER(GCHeapOomData, cdac_data::OomData) CDAC_GLOBAL_POINTER(GCHeapInternalRootArray, cdac_data::InternalRootArray) CDAC_GLOBAL_POINTER(GCHeapInternalRootArrayIndex, cdac_data::InternalRootArrayIndex) diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 501c93d154ad23..705fa92897d87f 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -190,7 +190,9 @@ inline void FATAL_GC_ERROR() #define FEATURE_PREMORTEM_FINALIZATION #define GC_HISTORY +#ifndef TARGET_WASM #define BACKGROUND_GC //concurrent background GC (requires WRITE_WATCH) +#endif //!TARGET_WASM // We need the lower 3 bits in the MT to do our bookkeeping so doubly linked free list is only for 64-bit #if defined(BACKGROUND_GC) && defined(HOST_64BIT) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs index 99e9ee879cdd4a..355331f61cea28 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs @@ -7,10 +7,14 @@ internal sealed class GCHeapWKS : IGCHeap { public GCHeapWKS(Target target) { - MarkArray = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapMarkArray)); - NextSweepObj = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapNextSweepObj)); - BackgroundMinSavedAddr = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMinSavedAddr)); - BackgroundMaxSavedAddr = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMaxSavedAddr)); + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapMarkArray, out TargetPointer? markArrayPtr)) + MarkArray = target.ReadPointer(markArrayPtr.Value); + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapNextSweepObj, out TargetPointer? nextSweepObjPtr)) + NextSweepObj = target.ReadPointer(nextSweepObjPtr.Value); + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapBackgroundMinSavedAddr, out TargetPointer? bgMinPtr)) + BackgroundMinSavedAddr = target.ReadPointer(bgMinPtr.Value); + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapBackgroundMaxSavedAddr, out TargetPointer? bgMaxPtr)) + BackgroundMaxSavedAddr = target.ReadPointer(bgMaxPtr.Value); AllocAllocated = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapAllocAllocated)); EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapEphemeralHeapSegment)); CardTable = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable)); @@ -41,10 +45,10 @@ public GCHeapWKS(Target target) FreeRegions = freeRegionsPtr.Value; } - public TargetPointer MarkArray { get; } - public TargetPointer NextSweepObj { get; } - public TargetPointer BackgroundMinSavedAddr { get; } - public TargetPointer BackgroundMaxSavedAddr { get; } + public TargetPointer? MarkArray { get; } + public TargetPointer? NextSweepObj { get; } + public TargetPointer? BackgroundMinSavedAddr { get; } + public TargetPointer? BackgroundMaxSavedAddr { get; } public TargetPointer AllocAllocated { get; } public TargetPointer EphemeralHeapSegment { get; } public TargetPointer CardTable { get; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs index 9fd8ced2e9ed2d..932a31af52e3e2 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs @@ -170,10 +170,10 @@ private GCHeapData GetGCHeapDataFromHeap(IGCHeap heap) return new GCHeapData() { - MarkArray = heap.MarkArray, - NextSweepObject = heap.NextSweepObj, - BackGroundSavedMinAddress = heap.BackgroundMinSavedAddr, - BackGroundSavedMaxAddress = heap.BackgroundMaxSavedAddr, + MarkArray = heap.MarkArray ?? TargetPointer.Null, + NextSweepObject = heap.NextSweepObj ?? TargetPointer.Null, + BackGroundSavedMinAddress = heap.BackgroundMinSavedAddr ?? TargetPointer.Null, + BackGroundSavedMaxAddress = heap.BackgroundMaxSavedAddr ?? TargetPointer.Null, AllocAllocated = heap.AllocAllocated, EphemeralHeapSegment = heap.EphemeralHeapSegment, CardTable = heap.CardTable, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs index f7e6dc1cc9ac77..4264e9611db325 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs @@ -5,10 +5,10 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; internal interface IGCHeap { - TargetPointer MarkArray { get; } - TargetPointer NextSweepObj { get; } - TargetPointer BackgroundMinSavedAddr { get; } - TargetPointer BackgroundMaxSavedAddr { get; } + TargetPointer? MarkArray { get; } + TargetPointer? NextSweepObj { get; } + TargetPointer? BackgroundMinSavedAddr { get; } + TargetPointer? BackgroundMaxSavedAddr { get; } TargetPointer AllocAllocated { get; } TargetPointer EphemeralHeapSegment { get; } TargetPointer CardTable { get; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs index 7acf5d75459357..0cd297968c8b6f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs @@ -14,10 +14,15 @@ public GCHeapSVR(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.GCHeap); - MarkArray = target.ReadPointerField(address, type, nameof(MarkArray)); - NextSweepObj = target.ReadPointerField(address, type, nameof(NextSweepObj)); - BackgroundMinSavedAddr = target.ReadPointerField(address, type, nameof(BackgroundMinSavedAddr)); - BackgroundMaxSavedAddr = target.ReadPointerField(address, type, nameof(BackgroundMaxSavedAddr)); + // Fields only exist in background GC builds + if (type.Fields.ContainsKey(nameof(MarkArray))) + MarkArray = target.ReadPointerField(address, type, nameof(MarkArray)); + if (type.Fields.ContainsKey(nameof(NextSweepObj))) + NextSweepObj = target.ReadPointerField(address, type, nameof(NextSweepObj)); + if (type.Fields.ContainsKey(nameof(BackgroundMinSavedAddr))) + BackgroundMinSavedAddr = target.ReadPointerField(address, type, nameof(BackgroundMinSavedAddr)); + if (type.Fields.ContainsKey(nameof(BackgroundMaxSavedAddr))) + BackgroundMaxSavedAddr = target.ReadPointerField(address, type, nameof(BackgroundMaxSavedAddr)); AllocAllocated = target.ReadPointerField(address, type, nameof(AllocAllocated)); EphemeralHeapSegment = target.ReadPointerField(address, type, nameof(EphemeralHeapSegment)); CardTable = target.ReadPointerField(address, type, nameof(CardTable)); @@ -49,10 +54,10 @@ public GCHeapSVR(Target target, TargetPointer address) FreeRegions = address + (ulong)type.Fields[nameof(FreeRegions)].Offset; } - public TargetPointer MarkArray { get; } - public TargetPointer NextSweepObj { get; } - public TargetPointer BackgroundMinSavedAddr { get; } - public TargetPointer BackgroundMaxSavedAddr { get; } + public TargetPointer? MarkArray { get; } + public TargetPointer? NextSweepObj { get; } + public TargetPointer? BackgroundMinSavedAddr { get; } + public TargetPointer? BackgroundMaxSavedAddr { get; } public TargetPointer AllocAllocated { get; } public TargetPointer EphemeralHeapSegment { get; } public TargetPointer CardTable { get; }