diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 01562fafebafd5..bbb6d3ebafb121 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -22,6 +22,15 @@ public readonly struct GCHeapData // Fields only valid in segment GC builds public TargetPointer SavedSweepEphemeralSegment { get; init; } public TargetPointer SavedSweepEphemeralStart { get; init; } + + public TargetPointer InternalRootArray { get; init; } + public TargetNUInt InternalRootArrayIndex { get; init; } + public bool HeapAnalyzeSuccess { get; init; } + + public IReadOnlyList InterestingData { get; init; } + public IReadOnlyList CompactReasons { get; init; } + public IReadOnlyList ExpandMechanisms { get; init; } + public IReadOnlyList InterestingMechanismBits { get; init; } } public readonly struct GCGenerationData @@ -31,6 +40,32 @@ public readonly struct GCGenerationData public TargetPointer AllocationContextPointer { get; init; } public TargetPointer AllocationContextLimit { get; init; } } + +public readonly struct GCHeapSegmentData +{ + public TargetPointer Allocated { get; init; } + public TargetPointer Committed { get; init; } + public TargetPointer Reserved { get; init; } + public TargetPointer Used { get; init; } + public TargetPointer Mem { get; init; } + public TargetNUInt Flags { get; init; } + public TargetPointer Next { get; init; } + public TargetPointer BackgroundAllocated { get; init; } + public TargetPointer Heap { get; init; } +} + +public readonly struct GCOomData +{ + public int Reason { get; init; } + public TargetNUInt AllocSize { get; init; } + public TargetPointer Reserved { get; init; } + public TargetPointer Allocated { get; init; } + public TargetNUInt GCIndex { get; init; } + public int Fgm { get; init; } + public TargetNUInt Size { get; init; } + public TargetNUInt AvailablePagefileMB { get; init; } + public bool LohP { get; init; } +} ``` ```csharp @@ -39,6 +74,7 @@ public readonly struct GCGenerationData // "workstation" or "server" // "segments" or "regions" // "background" + // "dynamic_heap" string[] GetGCIdentifiers(); // Return the number of GC heaps @@ -51,14 +87,26 @@ public readonly struct GCGenerationData void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr); // Gets the current GC state enum value uint GetCurrentGCState(); - // Returns pointers to all GC heaps. + // Gets the current GC heap dynamic adaptation mode + bool TryGetDynamicAdaptationMode(out int mode); + // Gets data on a GC heap segment + GCHeapSegmentData GetHeapSegmentData(TargetPointer segmentAddress); + // Gets the GlobalMechanisms list + IReadOnlyList GetGlobalMechanisms(); + // Returns pointers to all GC heaps IEnumerable GetGCHeaps(); - /* WKS only APIs */ - GCHeapData WKSGetHeapData(); + // The following APIs have both a workstation and serer variant. + // The workstation variant implitly operates on the global heap. + // The server variants allow passing in a heap pointer. + + // Gets data about a GC heap + GCHeapData GetHeapData(); + GCHeapData GetHeapData(TargetPointer heapAddress); - /* SVR only APIs */ - GCHeapData SVRGetHeapData(TargetPointer heapAddress); + // Gets data about a managed OOM occurance + GCOomData GetOomData(); + GCOomData GetOomData(TargetPointer heapAddress); ``` ## Version 1 @@ -77,10 +125,36 @@ Data descriptors used: | `GCHeap` | GenerationTable | GC | Pointer to the start of an array containing `"TotalGenerationCount"` `Generation` structures (in sever builds) | | `GCHeap` | SavedSweepEphemeralSeg | GC | Pointer to the heap's saved sweep ephemeral segment (only in server builds with segment) | | `GCHeap` | SavedSweepEphemeralStart | GC | Start of the heap's sweep ephemeral segment (only in server builds with segment) | +| `GCHeap` | OomData | GC | OOM related data in a struct (in sever builds) | +| `GCHeap` | InternalRootArray | GC | Data array stored per heap (in sever builds) | +| `GCHeap` | InternalRootArrayIndex | GC | Index into InternalRootArray (in sever builds) | +| `GCHeap` | HeapAnalyzeSuccess | GC | Boolean indicating if heap analyze succeeded (in sever builds) | +| `GCHeap` | InterestingData | GC | Data array stored per heap (in sever builds) | +| `GCHeap` | CompactReasons | GC | Data array stored per heap (in sever builds) | +| `GCHeap` | ExpandMechanisms | GC | Data array stored per heap (in sever builds) | +| `GCHeap` | InterestingMechanismBits | GC | Data array stored per heap (in sever builds) | | `Generation` | AllocationContext | GC | A `GCAllocContext` struct | | `Generation` | StartSegment | GC | Pointer to the start heap segment | | `Generation` | AllocationStart | GC | Pointer to the allocation start | | `CFinalize` | FillPointers | GC | Pointer to the start of an array containing `"CFinalizeFillPointersLength"` elements | +| `HeapSegment` | Allocated | GC | Pointer to the allocated memory in the heap segment | +| `HeapSegment` | Committed | GC | Pointer to the committed memory in the heap segment | +| `HeapSegment` | Reserved | GC | Pointer to the reserved memory in the heap segment | +| `HeapSegment` | Used | GC | Pointer to the used memory in the heap segment | +| `HeapSegment` | Mem | GC | Pointer to the start of the heap segment memory | +| `HeapSegment` | Flags | GC | Flags indicating the heap segment properties | +| `HeapSegment` | Next | GC | Pointer to the next heap segment | +| `HeapSegment` | BackgroundAllocated | GC | Pointer to the background allocated memory in the heap segment | +| `HeapSegment` | Heap | GC | Pointer to the heap that owns this segment (only in server builds) | +| `OomHistory` | Reason | GC | Reason code for the out-of-memory condition | +| `OomHistory` | AllocSize | GC | Size of the allocation that caused the OOM | +| `OomHistory` | Reserved | GC | Pointer to reserved memory at time of OOM | +| `OomHistory` | Allocated | GC | Pointer to allocated memory at time of OOM | +| `OomHistory` | GcIndex | GC | GC index when the OOM occurred | +| `OomHistory` | Fgm | GC | Foreground GC marker value | +| `OomHistory` | Size | GC | Size value related to the OOM condition | +| `OomHistory` | AvailablePagefileMb | GC | Available pagefile size in MB at time of OOM | +| `OomHistory` | LohP | GC | Large object heap flag indicating if OOM was related to LOH | | `GCAllocContext` | Pointer | VM | Current GCAllocContext pointer | | `GCAllocContext` | Limit | VM | Pointer to the GCAllocContext limit | @@ -94,6 +168,10 @@ Global variables used: | `MaxGeneration` | TargetPointer | GC | Pointer to the maximum generation number (uint) | | `TotalGenerationCount` | uint | GC | The total number of generations in the GC | | `CFinalizeFillPointersLength` | uint | GC | The number of elements in the `CFinalize::FillPointers` array | +| `InterestingDataLength` | uint | GC | The number of elements in the `InterestingData` array | +| `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) | @@ -105,6 +183,16 @@ Global variables used: | `GCHeapGenerationTable` | TargetPointer | GC | Pointer to the start of an array containing `"TotalGenerationCount"` `Generation` structures (in workstation builds) | | `GCHeapSavedSweepEphemeralSeg` | TargetPointer | GC | Pointer to the static heap's saved sweep ephemeral segment (in workstation builds with segment) | | `GCHeapSavedSweepEphemeralStart` | TargetPointer | GC | Start of the static heap's sweep ephemeral segment (in workstation builds with segment) | +| `GCHeapOomData` | TargetPointer | GC | OOM related data in a struct (in workstation builds) | +| `GCHeapInternalRootArray` | TargetPointer | GC | Data array stored per heap (in workstation builds) | +| `GCHeapInternalRootArrayIndex` | TargetPointer | GC | Index into InternalRootArray (in workstation builds) | +| `GCHeapHeapAnalyzeSuccess` | TargetPointer | GC | Boolean indicating if heap analyze succeeded (in workstation builds) | +| `GCHeapInterestingData` | TargetPointer | GC | Data array stored per heap (in workstation builds) | +| `GCHeapCompactReasons` | TargetPointer | GC | Data array stored per heap (in workstation builds) | +| `GCHeapExpandMechanisms` | TargetPointer | GC | Data array stored per heap (in workstation builds) | +| `GCHeapInterestingMechanismBits` | TargetPointer | GC | Data array stored per heap (in workstation builds) | +| `CurrentGCState` | uint | GC | `c_gc_state` enum value. Only available when `GCIdentifiers` contains `background`. | +| `DynamicAdaptationMode | int | GC | GC heap dynamic adaptation mode. Only available when `GCIdentifiers` contains `dynamic_heap`. | | `GCLowestAddress` | TargetPointer | VM | Lowest GC address as recorded by the VM/GC interface | | `GCHighestAddress` | TargetPointer | VM | Highest GC address as recorded by the VM/GC interface | @@ -123,7 +211,8 @@ Constants used: GCHeapType IGC.GetGCIdentifiers() { string gcIdentifiers = target.ReadGlobalString("GCIdentifiers"); - return gcIdentifiers.Split(", "); + return gcIdentifiers.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + } uint IGC.GetGCHeapCount() @@ -164,7 +253,7 @@ void IGC.GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr) uint IGC.GetCurrentGCState() { string[] gcIdentifiers = GetGCIdentifiers(); - if (gcType.Contains("background")) + if (gcIdentifiers.Contains("background")) { return target.Read(target.ReadGlobalPointer("CurrentGCState")); } @@ -172,6 +261,52 @@ uint IGC.GetCurrentGCState() return 0; } +bool IGC.TryGetDynamicAdaptationMode(out int mode) +{ + mode = default; + string[] gcIdentifiers = GetGCIdentifiers(); + if (!gcIdentifiers.Contains("dynamic_heap")) + { + return false; + } + + mode = target.read(target.ReadGlobalPointer("DynamicAdaptationMode")); + return true; +} + +GCHeapSegmentData IGC.GetGCHeapSegmentData(TargetPointer segmentAddress) +{ + GCHeapSegmentData data = default; + + data.Allocated = target.ReadPointer(segmentAddress + /* HeapSegment::Allocated offset */); + data.Committed = target.ReadPointer(segmentAddress + /* HeapSegment::Committed offset */); + data.Reserved = target.ReadPointer(segmentAddress + /* HeapSegment::Reserved offset */); + data.Used = target.ReadPointer(segmentAddress + /* HeapSegment::Used offset */); + data.Mem = target.ReadPointer(segmentAddress + /* HeapSegment::Mem offset */); + data.Flags = target.ReadNUInt(segmentAddress + /* HeapSegment::Flags offset */); + data.Next = target.ReadPointer(segmentAddress + /* HeapSegment::Next offset */); + data.BackGroundAllocated = target.ReadPointer(segmentAddress + /* HeapSegment::BackGroundAllocated offset */); + + if (/* HeapSegment::Heap offset */) + { + data.Heap = target.ReadPointer(segmentAddress + /* HeapSegment::Heap offset */); + } + else + { + data.Heap = TargetPointer.Null; + } + + return data; +} + +IReadOnlyList IGC.GetGlobalMechanisms() +{ + if (!target.TryReadGlobalPointer("GCGlobalMechanisms", out TargetPointer? globalMechanismsArrayStart)) + return Array.Empty(); + uint globalMechanismsLength = target.ReadGlobal("GlobalMechanismsLength"); + return ReadGCHeapDataArray(globalMechanismsArrayStart.Value, globalMechanismsLength); +} + IEnumerable IGC.GetGCHeaps() { string[] gcIdentifiers = GetGCIdentifiers(); @@ -189,9 +324,49 @@ IEnumerable IGC.GetGCHeaps() } ``` -workstation GC only APIs +GetOomData +```csharp +GCOomData IGC.GetOomData() +{ + string[] gcIdentifiers = GetGCIdentifiers(); + if (!gcType.Contains("workstation")) + throw new InvalidOperationException(); + + TargetPointer oomHistory = target.ReadGlobalPointer("GCHeapOomData"); + return GetGCOomData(oomHistoryData); +} + +GCOomData IGC.GetOomData(TargetPointer heapAddress) +{ + string[] gcIdentifiers = GetGCIdentifiers(); + if (!gcType.Contains("server")) + throw new InvalidOperationException(); + + TargetPointer oomHistory = target.ReadPointer(heapAddress + /* GCHeap::OomData offset */); + return GetGCOomData(oomHistory); +} + +private GCOomData GetGCOomData(TargetPointer oomHistory) +{ + GCOomData data = default; + + data.Reason = target.Read(oomHistory + /* OomHistory::Reason offset */); + data.AllocSize = target.ReadNUInt(oomHistory + /* OomHistory::AllocSize offset */); + data.Reserved = target.ReadPointer(oomHistory + /* OomHistory::Reserved offset */); + data.Allocated = target.ReadPointer(oomHistory + /* OomHistory::Allocated offset */); + data.GcIndex = target.ReadNUInt(oomHistory + /* OomHistory::GcIndex offset */); + data.Fgm = target.Read(oomHistory + /* OomHistory::Fgm offset */); + data.Size = target.ReadNUInt(oomHistory + /* OomHistory::Size offset */); + data.AvailablePagefileMb = target.ReadNUInt(oomHistory + /* OomHistory::AvailablePagefileMb offset */); + data.LohP = target.Read(oomHistory + /* OomHistory::LohP offset */); + + return data; +} +``` + +GetHeapData ```csharp -GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) +GCHeapData IGC.GetHeapData() { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("workstation")) @@ -208,6 +383,14 @@ GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) data.EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer("GCHeapEphemeralHeapSegment")); data.CardTable = target.ReadPointer(target.ReadGlobalPointer("GCHeapCardTable")); + // Read GenerationTable + TargetPointer generationTableArrayStart = target.ReadGlobalPointer("GCHeapGenerationTable"); + data.GenerationTable = GetGenerationData(generationTableArrayStart); + + // Read finalize queue from global and CFinalize offsets + TargetPointer finalizeQueue = target.ReadPointer(target.ReadGlobalPointer("GCHeapFinalizeQueue")); + data.FillPointers = GetCFinalizeFillPointers(finalizeQueue); + if (target.TryReadGlobalPointer("GCHeapSavedSweepEphemeralSeg", out TargetPointer? savedSweepEphemeralSegPtr)) { data.SavedSweepEphemeralSeg = target.ReadPointer(savedSweepEphemeralSegPtr.Value); @@ -226,49 +409,31 @@ GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) data.SavedSweepEphemeralStart = 0; } - // Read GenerationTable - TargetPointer generationTableArrayStart = target.ReadGlobalPointer("GCHeapGenerationTable"); - uint generationTableLength = target.ReadGlobal("TotalGenerationCount"); - uint generationSize = target.GetTypeInfo(DataType.Generation).Size; - - List generationTable = [] - for (uint i = 0; i < generationTableLength; i++) - { - GCGenerationData generationData; - TargetPointer generationAddress = generationTableArrayStart + (i * generationSize); - generationData.StartSegment = target.ReadPointer(generationAddress + /* Generation::StartSegment offset */); - if (/* Generation::AllocationStart is present */) - generationData.AllocationStart = target.ReadPointer(generationAddress + /* Generation::AllocationStart offset */) - else - generationData.AllocationStart = -1; - - generationData.AllocationContextPointer = - target.ReadPointer(generationAddress + /* Generation::AllocationContext offset */ + /* GCAllocContext::Pointer offset */); - generationData.AllocationContextLimit = - target.ReadPointer(generationAddress + /* Generation::AllocationContext offset */ + /* GCAllocContext::Limit offset */); - - generationTable.Add(generationData); - } - data.GenerationTable = generationTable; - - // Read finalize queue from global and CFinalize offsets - TargetPointer finalizeQueue = target.ReadPointer(target.ReadGlobalPointer("GCHeapFinalizeQueue")); - TargetPointer fillPointersArrayStart = finalizeQueue + /* CFinalize::FillPointers offset */; - uint fillPointersLength = target.ReadGlobal("CFinalizeFillPointersLength"); - - List fillPointers = []; - for (uint i = 0; i < fillPointersLength; i++) - fillPointers[i] = target.ReadPointer(fillPointersArrayStart + (i * target.PointerSize)); - - data.FillPointers = fillPointers; + data.InternalRootArray = target.ReadPointer(target.ReadGlobalPointer("GCHeapInternalRootArray")); + data.InternalRootArrayIndex = target.ReadNUInt(target.ReadGlobalPointer("GCHeapInternalRootArrayIndex")); + data.HeapAnalyzeSuccess = target.Read(target.ReadGlobalPointer("GCHeapHeapAnalyzeSuccess")); + + TargetPointer interestingDataStartAddr = target.ReadGlobalPointer("GCHeapInterestingData"); + data.InterestingData = ReadGCHeapDataArray( + interestingDataStartAddr, + target.ReadGlobal("InterestingDataLength")); + TargetPointer compactReasonsStartAddr = target.ReadGlobalPointer("GCHeapCompactReasons"); + data.CompactReasons = ReadGCHeapDataArray( + compactReasonsStartAddr, + target.ReadGlobal("CompactReasonsLength")); + TargetPointer expandMechanismsStartAddr = target.ReadGlobalPointer("GCHeapExpandMechanisms"); + data.ExpandMechanisms = ReadGCHeapDataArray( + expandMechanismsStartAddr, + target.ReadGlobal("ExpandMechanismsLength")); + TargetPointer interestingMechanismBitsStartAddr = target.ReadGlobalPointer("GCHeapInterestingMechanismBits"); + data.InterestingMechanismBits = ReadGCHeapDataArray( + interestingMechanismBitsStartAddr, + target.ReadGlobal("InterestingMechanismBitsLength")); return data; } -``` -server GC only APIs -```csharp -GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) +GCHeapData IGC.GetHeapData(TargetPointer heapAddress) { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("server")) @@ -285,6 +450,15 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) data.EphemeralHeapSegment = target.ReadPointer(heapAddress + /* GCHeap::EphemeralHeapSegment offset */); data.CardTable = target.ReadPointer(heapAddress + /* GCHeap::CardTable offset */); + // Read GenerationTable + TargetPointer generationTableArrayStart = heapAddress + /* GCHeap::GenerationTable offset */; + data.GenerationTable = GetGenerationData(generationTableArrayStart); + + // Read finalize queue fill pointers + TargetPointer finalizeQueue = target.ReadPointer(heapAddress + /* GCHeap::FinalizeQueue offset */); + data.FillPointers = GetCFinalizeFillPointers(finalizeQueue); + + if (/* GCHeap::SavedSweepEphemeralSeg is present */) { data.SavedSweepEphemeralSeg = target.ReadPointer(heapAddress + /* GCHeap::SavedSweepEphemeralSeg offset */); @@ -303,12 +477,36 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) data.SavedSweepEphemeralStart = 0; } - // Read GenerationTable - TargetPointer generationTableArrayStart = heapAddress + /* GCHeap::GenerationTable offset */; + data.InternalRootArray = target.ReadPointer(heapAddress + /* GCHeap::InternalRootArray offset */); + data.InternalRootArrayIndex = target.ReadNUInt(heapAddress + /* GCHeap::InternalRootArrayIndex offset */); + data.HeapAnalyzeSuccess = target.Read(heapAddress + /* GCHeap::HeapAnalyzeSuccess offset */); + + TargetPointer interestingDataStartAddr = heapAddress + /* GCHeap::InterestingData offset */; + data.InterestingData = ReadGCHeapDataArray( + interestingDataStartAddr, + target.ReadGlobal("InterestingDataLength")); + TargetPointer compactReasonsStartAddr = heapAddress + /* GCHeap::CompactReasons offset */; + data.CompactReasons = ReadGCHeapDataArray( + compactReasonsStartAddr, + target.ReadGlobal("CompactReasonsLength")); + TargetPointer expandMechanismsStartAddr = heapAddress + /* GCHeap::ExpandMechanisms offset */; + data.ExpandMechanisms = ReadGCHeapDataArray( + expandMechanismsStartAddr, + target.ReadGlobal("ExpandMechanismsLength")); + TargetPointer interestingMechanismBitsStartAddr = heapAddress + /* GCHeap::InterestingMechanismBits offset */; + data.InterestingMechanismBits = ReadGCHeapDataArray( + interestingMechanismBitsStartAddr, + target.ReadGlobal("InterestingMechanismBitsLength")); + + return data; +} + +private List GetGenerationData(TargetPointer generationTableArrayStart) +{ uint generationTableLength = target.ReadGlobal("TotalGenerationCount"); uint generationSize = target.GetTypeInfo(DataType.Generation).Size; - List generationTable = [] + List generationTable = []; for (uint i = 0; i < generationTableLength; i++) { GCGenerationData generationData; @@ -326,20 +524,27 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) generationTable.Add(generationData); } - data.GenerationTable = generationTable; - // Read finalize queue from global and CFinalize offsets - TargetPointer finalizeQueue = target.ReadPointer(heapAddress + /* GCHeap::FinalizeQueue offset */); - TargetPointer fillPointersArrayStart = finalizeQueue + /* CFinalize::FillPointers offset */; + return generationTable; +} + +private List GetCFinalizeFillPointers(TargetPointer cfinalize) +{ + TargetPointer fillPointersArrayStart = cfinalize + /* CFinalize::FillPointers offset */; uint fillPointersLength = target.ReadGlobal("CFinalizeFillPointersLength"); List fillPointers = []; for (uint i = 0; i < fillPointersLength; i++) fillPointers[i] = target.ReadPointer(fillPointersArrayStart + (i * target.PointerSize)); - - data.FillPointers = fillPointers; - return data; + return fillPointers; } -``` +private List ReadGCHeapDataArray(TargetPointer arrayStart, uint length) +{ + List arr = []; + for (uint i = 0; i < length; i++) + arr.Add(target.ReadNUInt(arrayStart + (i * target.PointerSize))); + return arr; +} +``` diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index d21cccb71d8956..d77cf6e3e2a8ad 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -41,10 +41,16 @@ struct cdac_data #ifdef BACKGROUND_GC static constexpr c_gc_state* CurrentGCState = const_cast(&GC_NAMESPACE::gc_heap::current_c_gc_state); #endif // BACKGROUND_GC +#ifdef DYNAMIC_HEAP_COUNT + static constexpr int* DynamicAdaptationMode = &GC_NAMESPACE::gc_heap::dynamic_adaptation_mode; +#endif // DYNAMIC_HEAP_COUNT #ifdef SERVER_GC static constexpr GC_NAMESPACE::gc_heap*** Heaps = &GC_NAMESPACE::gc_heap::g_heaps; #endif // SERVER_GC + GC_HEAP_FIELD(OomData, oom_info) + + /* For use in GCHeapDetails APIs */ GC_HEAP_FIELD(MarkArray, mark_array) GC_HEAP_FIELD(NextSweepObj, next_sweep_obj) GC_HEAP_FIELD(BackgroundMinSavedAddr, background_saved_lowest_address) @@ -53,13 +59,22 @@ struct cdac_data 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 GC_HEAP_FIELD(SavedSweepEphemeralSeg, saved_sweep_ephemeral_seg) GC_HEAP_FIELD(SavedSweepEphemeralStart, saved_sweep_ephemeral_start) #endif // !USE_REGIONS + + /* For use in GCHeapAnalyzeData APIs */ + GC_HEAP_FIELD(InternalRootArray, internal_root_array) + GC_HEAP_FIELD(InternalRootArrayIndex, internal_root_array_index) + GC_HEAP_FIELD(HeapAnalyzeSuccess, heap_analyze_success) + + /* For use in GCInterestingInfo APIs */ + GC_HEAP_FIELD(InterestingData, interesting_data_per_heap) + GC_HEAP_FIELD(CompactReasons, compact_reasons_per_heap) + GC_HEAP_FIELD(ExpandMechanisms, expand_mechanisms_per_heap) + GC_HEAP_FIELD(InterestingMechanismBits, interesting_mechanism_bits_per_heap) }; template<> diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index ac20f6cc00fc60..3b97e4d8f2a280 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -26,6 +26,14 @@ CDAC_TYPE_FIELD(GCHeap, /*pointer*/, GenerationTable, cdac_data::SavedSweepEphemeralSeg) CDAC_TYPE_FIELD(GCHeap, /*pointer*/, SavedSweepEphemeralStart, cdac_data::SavedSweepEphemeralStart) #endif // !USE_REGIONS +CDAC_TYPE_FIELD(GCHeap, /*oom_history*/, OomData, cdac_data::OomData) +CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InternalRootArray, cdac_data::InternalRootArray) +CDAC_TYPE_FIELD(GCHeap, /*nuint*/, InternalRootArrayIndex, cdac_data::InternalRootArrayIndex) +CDAC_TYPE_FIELD(GCHeap, /*int*/, HeapAnalyzeSuccess, cdac_data::HeapAnalyzeSuccess) +CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InterestingData, cdac_data::InterestingData) +CDAC_TYPE_FIELD(GCHeap, /*pointer*/, CompactReasons, cdac_data::CompactReasons) +CDAC_TYPE_FIELD(GCHeap, /*pointer*/, ExpandMechanisms, cdac_data::ExpandMechanisms) +CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InterestingMechanismBits, cdac_data::InterestingMechanismBits) CDAC_TYPE_END(GCHeap) #endif // SERVER_GC @@ -43,12 +51,46 @@ CDAC_TYPE_INDETERMINATE(CFinalize) CDAC_TYPE_FIELD(CFinalize, /*pointer*/, FillPointers, cdac_data::FillPointers) CDAC_TYPE_END(CFinalize) +CDAC_TYPE_BEGIN(HeapSegment) +CDAC_TYPE_INDETERMINATE(HeapSegment) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Allocated, offsetof(GC_NAMESPACE::heap_segment, allocated)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Committed, offsetof(GC_NAMESPACE::heap_segment, committed)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Reserved, offsetof(GC_NAMESPACE::heap_segment, reserved)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Used, offsetof(GC_NAMESPACE::heap_segment, used)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Mem, offsetof(GC_NAMESPACE::heap_segment, mem)) +CDAC_TYPE_FIELD(HeapSegment, /*nuint*/, Flags, offsetof(GC_NAMESPACE::heap_segment, flags)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Next, offsetof(GC_NAMESPACE::heap_segment, next)) +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, BackgroundAllocated, offsetof(GC_NAMESPACE::heap_segment, background_allocated)) +#ifdef MULTIPLE_HEAPS +CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Heap, offsetof(GC_NAMESPACE::heap_segment, heap)) +#endif // MULTIPLE_HEAPS +CDAC_TYPE_END(HeapSegment) + +CDAC_TYPE_BEGIN(OomHistory) +CDAC_TYPE_INDETERMINATE(OomHistory) +CDAC_TYPE_FIELD(OomHistory, /*int32*/, Reason, offsetof(oom_history, reason)) +CDAC_TYPE_FIELD(OomHistory, /*nuint*/, AllocSize, offsetof(oom_history, alloc_size)) +CDAC_TYPE_FIELD(OomHistory, /*pointer*/, Reserved, offsetof(oom_history, reserved)) +CDAC_TYPE_FIELD(OomHistory, /*pointer*/, Allocated, offsetof(oom_history, allocated)) +CDAC_TYPE_FIELD(OomHistory, /*nuint*/, GcIndex, offsetof(oom_history, gc_index)) +CDAC_TYPE_FIELD(OomHistory, /*int32*/, Fgm, offsetof(oom_history, fgm)) +CDAC_TYPE_FIELD(OomHistory, /*nuint*/, Size, offsetof(oom_history, size)) +CDAC_TYPE_FIELD(OomHistory, /*nuint*/, AvailablePagefileMb, offsetof(oom_history, available_pagefile_mb)) +CDAC_TYPE_FIELD(OomHistory, /*uint32*/, LohP, offsetof(oom_history, loh_p)) +CDAC_TYPE_END(OomHistory) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() CDAC_GLOBAL(TotalGenerationCount, /*uint32*/, (uint32_t)total_generation_count) CDAC_GLOBAL(CFinalizeFillPointersLength, /*uint32*/, (uint32_t)cdac_data::FillPointersLength) +CDAC_GLOBAL(InterestingDataLength, /*uint32*/, NUM_GC_DATA_POINTS) +CDAC_GLOBAL(CompactReasonsLength, /*uint32*/, MAX_COMPACT_REASONS_COUNT) +CDAC_GLOBAL(ExpandMechanismsLength, /*uint32*/, MAX_EXPAND_MECHANISMS_COUNT) +CDAC_GLOBAL(InterestingMechanismBitsLength, /*uint32*/, MAX_GC_MECHANISM_BITS_COUNT) +CDAC_GLOBAL(GlobalMechanismsLength, /*uint32*/, MAX_GLOBAL_GC_MECHANISMS_COUNT) + #ifndef SERVER_GC CDAC_GLOBAL_POINTER(GCHeapMarkArray, cdac_data::MarkArray) @@ -64,6 +106,14 @@ CDAC_GLOBAL_POINTER(GCHeapGenerationTable, cdac_data::Gen CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralSeg, cdac_data::SavedSweepEphemeralSeg) CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralStart, cdac_data::SavedSweepEphemeralStart) #endif // !USE_REGIONS +CDAC_GLOBAL_POINTER(GCHeapOomData, cdac_data::OomData) +CDAC_GLOBAL_POINTER(GCHeapInternalRootArray, cdac_data::InternalRootArray) +CDAC_GLOBAL_POINTER(GCHeapInternalRootArrayIndex, cdac_data::InternalRootArrayIndex) +CDAC_GLOBAL_POINTER(GCHeapHeapAnalyzeSuccess, cdac_data::HeapAnalyzeSuccess) +CDAC_GLOBAL_POINTER(GCHeapInterestingData, cdac_data::InterestingData) +CDAC_GLOBAL_POINTER(GCHeapCompactReasons, cdac_data::CompactReasons) +CDAC_GLOBAL_POINTER(GCHeapExpandMechanisms, cdac_data::ExpandMechanisms) +CDAC_GLOBAL_POINTER(GCHeapInterestingMechanismBits, cdac_data::InterestingMechanismBits) #endif // !SERVER_GC #ifdef SERVER_GC @@ -78,15 +128,22 @@ CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralStart, cdac_data::Heaps) CDAC_GLOBAL_POINTER(CurrentGCState, cdac_data::CurrentGCState) #endif // BACKGROUND_GC +#ifdef DYNAMIC_HEAP_COUNT +CDAC_GLOBAL_POINTER(DynamicAdaptationMode, cdac_data::DynamicAdaptationMode) +#endif // DYNAMIC_HEAP_COUNT + +#ifdef GC_CONFIG_DRIVEN +CDAC_GLOBAL_POINTER(GCGlobalMechanisms, &gc_global_mechanisms) +#endif // GC_CONFIG_DRIVEN + CDAC_GLOBAL_CONTRACT(GC, 1) CDAC_GLOBALS_END() diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGC.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGC.cs index 8a349d9d827e54..e0213bc48a086c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGC.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGC.cs @@ -15,6 +15,8 @@ public static class GCIdentifiers public const string Segments = "segments"; public const string Background = "background"; + + public const string DynamicHeapCount = "dynamic_heap"; } public readonly struct GCHeapData @@ -23,17 +25,22 @@ public readonly struct GCHeapData public TargetPointer NextSweepObject { get; init; } public TargetPointer BackGroundSavedMinAddress { get; init; } public TargetPointer BackGroundSavedMaxAddress { get; init; } - public TargetPointer AllocAllocated { get; init; } public TargetPointer EphemeralHeapSegment { get; init; } public TargetPointer CardTable { get; init; } public IReadOnlyList GenerationTable { get; init; } - public IReadOnlyList FillPointers { get; init; } + public TargetPointer SavedSweepEphemeralSegment { get; init; } /* Only valid in segment GC builds */ + public TargetPointer SavedSweepEphemeralStart { get; init; } /* Only valid in segment GC builds */ - // Fields only valid in segment GC builds - public TargetPointer SavedSweepEphemeralSegment { get; init; } - public TargetPointer SavedSweepEphemeralStart { get; init; } + public TargetPointer InternalRootArray { get; init; } + public TargetNUInt InternalRootArrayIndex { get; init; } + public bool HeapAnalyzeSuccess { get; init; } + + public IReadOnlyList InterestingData { get; init; } + public IReadOnlyList CompactReasons { get; init; } + public IReadOnlyList ExpandMechanisms { get; init; } + public IReadOnlyList InterestingMechanismBits { get; init; } } public readonly struct GCGenerationData @@ -44,6 +51,32 @@ public readonly struct GCGenerationData public TargetPointer AllocationContextLimit { get; init; } } +public readonly struct GCHeapSegmentData +{ + public TargetPointer Allocated { get; init; } + public TargetPointer Committed { get; init; } + public TargetPointer Reserved { get; init; } + public TargetPointer Used { get; init; } + public TargetPointer Mem { get; init; } + public TargetNUInt Flags { get; init; } + public TargetPointer Next { get; init; } + public TargetPointer BackgroundAllocated { get; init; } + public TargetPointer Heap { get; init; } +} + +public readonly struct GCOomData +{ + public int Reason { get; init; } + public TargetNUInt AllocSize { get; init; } + public TargetPointer Reserved { get; init; } + public TargetPointer Allocated { get; init; } + public TargetNUInt GCIndex { get; init; } + public int Fgm { get; init; } + public TargetNUInt Size { get; init; } + public TargetNUInt AvailablePagefileMB { get; init; } + public bool LohP { get; init; } +} + public interface IGC : IContract { static string IContract.Name { get; } = nameof(GC); @@ -55,13 +88,20 @@ public interface IGC : IContract uint GetMaxGeneration() => throw new NotImplementedException(); void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr) => throw new NotImplementedException(); uint GetCurrentGCState() => throw new NotImplementedException(); + bool TryGetGCDynamicAdaptationMode(out int mode) => throw new NotImplementedException(); + GCHeapSegmentData GetHeapSegmentData(TargetPointer segmentAddress) => throw new NotImplementedException(); + IReadOnlyList GetGlobalMechanisms() => throw new NotImplementedException(); IEnumerable GetGCHeaps() => throw new NotImplementedException(); - /* WKS only APIs */ - GCHeapData WKSGetHeapData() => throw new NotImplementedException(); + // workstation variant + GCHeapData GetHeapData() => throw new NotImplementedException(); + // server variant + GCHeapData GetHeapData(TargetPointer heapAddress) => throw new NotImplementedException(); - /* SVR only APIs */ - GCHeapData SVRGetHeapData(TargetPointer heapAddress) => throw new NotImplementedException(); + // workstation variant + GCOomData GetOomData() => throw new NotImplementedException(); + // server variant + GCOomData GetOomData(TargetPointer heapAddress) => throw new NotImplementedException(); } public readonly struct GC : IGC diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index d2cf878cb8a6c7..1539c1fdd922f2 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -140,4 +140,6 @@ public enum DataType GCHeap, Generation, CFinalize, + HeapSegment, + OomHistory, } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index 084afe879f99c1..bb54960759dc61 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -86,8 +86,16 @@ public static class Globals public const string NumHeaps = nameof(NumHeaps); public const string Heaps = nameof(Heaps); public const string CurrentGCState = nameof(CurrentGCState); - public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength); + public const string DynamicAdaptationMode = nameof(DynamicAdaptationMode); + public const string GCGlobalMechanisms = nameof(GCGlobalMechanisms); + public const string TotalGenerationCount = nameof(TotalGenerationCount); + public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength); + public const string InterestingDataLength = nameof(InterestingDataLength); + public const string CompactReasonsLength = nameof(CompactReasonsLength); + public const string ExpandMechanismsLength = nameof(ExpandMechanismsLength); + public const string InterestingMechanismBitsLength = nameof(InterestingMechanismBitsLength); + public const string GlobalMechanismsLength = nameof(GlobalMechanismsLength); public const string GCHeapMarkArray = nameof(GCHeapMarkArray); public const string GCHeapNextSweepObj = nameof(GCHeapNextSweepObj); @@ -100,6 +108,14 @@ public static class Globals public const string GCHeapGenerationTable = nameof(GCHeapGenerationTable); public const string GCHeapSavedSweepEphemeralSeg = nameof(GCHeapSavedSweepEphemeralSeg); public const string GCHeapSavedSweepEphemeralStart = nameof(GCHeapSavedSweepEphemeralStart); + public const string GCHeapOomData = nameof(GCHeapOomData); + public const string GCHeapInternalRootArray = nameof(GCHeapInternalRootArray); + public const string GCHeapInternalRootArrayIndex = nameof(GCHeapInternalRootArrayIndex); + public const string GCHeapHeapAnalyzeSuccess = nameof(GCHeapHeapAnalyzeSuccess); + public const string GCHeapInterestingData = nameof(GCHeapInterestingData); + public const string GCHeapCompactReasons = nameof(GCHeapCompactReasons); + public const string GCHeapExpandMechanisms = nameof(GCHeapExpandMechanisms); + public const string GCHeapInterestingMechanismBits = nameof(GCHeapInterestingMechanismBits); } public static class FieldNames { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCFactory.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCFactory.cs similarity index 100% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCFactory.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCFactory.cs 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 new file mode 100644 index 00000000000000..412c6c71a620cb --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; + +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)); + AllocAllocated = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapAllocAllocated)); + EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapEphemeralHeapSegment)); + CardTable = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable)); + FinalizeQueue = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapFinalizeQueue)); + GenerationTable = target.ReadGlobalPointer(Constants.Globals.GCHeapGenerationTable); + + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralSeg, out TargetPointer? savedSweepEphemeralSegPtr)) + SavedSweepEphemeralSeg = target.ReadPointer(savedSweepEphemeralSegPtr.Value); + if (target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralStart, out TargetPointer? savedSweepEphemeralStartPtr)) + SavedSweepEphemeralStart = target.ReadPointer(savedSweepEphemeralStartPtr.Value); + + OomData = target.ProcessedData.GetOrAdd(target.ReadGlobalPointer(Constants.Globals.GCHeapOomData)); + + InternalRootArray = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapInternalRootArray)); + InternalRootArrayIndex = target.ReadNUInt(target.ReadGlobalPointer(Constants.Globals.GCHeapInternalRootArrayIndex)); + HeapAnalyzeSuccess = target.Read(target.ReadGlobalPointer(Constants.Globals.GCHeapHeapAnalyzeSuccess)) != 0; + + InterestingData = target.ReadGlobalPointer(Constants.Globals.GCHeapInterestingData); + CompactReasons = target.ReadGlobalPointer(Constants.Globals.GCHeapCompactReasons); + ExpandMechanisms = target.ReadGlobalPointer(Constants.Globals.GCHeapExpandMechanisms); + InterestingMechanismBits = target.ReadGlobalPointer(Constants.Globals.GCHeapInterestingMechanismBits); + } + + 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; } + public TargetPointer FinalizeQueue { get; } + public TargetPointer GenerationTable { get; } + + public TargetPointer? SavedSweepEphemeralSeg { get; } + public TargetPointer? SavedSweepEphemeralStart { get; } + + public Data.OomHistory OomData { get; } + + public TargetPointer InternalRootArray { get; } + public TargetNUInt InternalRootArrayIndex { get; } + public bool HeapAnalyzeSuccess { get; } + + public TargetPointer InterestingData { get; } + public TargetPointer CompactReasons { get; } + public TargetPointer ExpandMechanisms { get; } + public TargetPointer InterestingMechanismBits { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs similarity index 54% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC_1.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs index 08183448c0c32d..cf101d271b3767 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -29,7 +29,7 @@ internal GC_1(Target target) string[] IGC.GetGCIdentifiers() { string gcIdentifiers = _target.ReadGlobalString(Constants.Globals.GCIdentifiers); - return gcIdentifiers.Split(", "); + return gcIdentifiers.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); } uint IGC.GetGCHeapCount() @@ -72,6 +72,40 @@ uint IGC.GetCurrentGCState() return _target.Read(_target.ReadGlobalPointer(Constants.Globals.CurrentGCState)); } + bool IGC.TryGetGCDynamicAdaptationMode(out int mode) + { + mode = default; + if (!IsDatasEnabled()) + return false; + mode = _target.Read(_target.ReadGlobalPointer(Constants.Globals.DynamicAdaptationMode)); + return true; + } + + GCHeapSegmentData IGC.GetHeapSegmentData(TargetPointer segmentAddress) + { + Data.HeapSegment heapSegment = _target.ProcessedData.GetOrAdd(segmentAddress); + return new GCHeapSegmentData() + { + Allocated = heapSegment.Allocated, + Committed = heapSegment.Committed, + Reserved = heapSegment.Reserved, + Used = heapSegment.Used, + Mem = heapSegment.Mem, + Flags = heapSegment.Flags, + Next = heapSegment.Next, + BackgroundAllocated = heapSegment.BackgroundAllocated, + Heap = heapSegment.Heap ?? TargetPointer.Null, + }; + } + + IReadOnlyList IGC.GetGlobalMechanisms() + { + if (!_target.TryReadGlobalPointer(Constants.Globals.GCGlobalMechanisms, out TargetPointer? globalMechanismsArrayStart)) + return Array.Empty(); + uint globalMechanismsLength = _target.ReadGlobal(Constants.Globals.GlobalMechanismsLength); + return ReadGCHeapDataArray(globalMechanismsArrayStart.Value, globalMechanismsLength); + } + IEnumerable IGC.GetGCHeaps() { if (GetGCType() != GCType.Server) @@ -85,54 +119,25 @@ IEnumerable IGC.GetGCHeaps() } } - GCHeapData IGC.WKSGetHeapData() + GCHeapData IGC.GetHeapData() { if (GetGCType() != GCType.Workstation) - throw new InvalidOperationException("WKSGetHeapData is only valid for Workstation GC."); - - TargetPointer markArray = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapMarkArray)); - TargetPointer nextSweepObj = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapNextSweepObj)); - TargetPointer backgroundMinSavedAddr = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMinSavedAddr)); - TargetPointer backgroundMaxSavedAddr = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMaxSavedAddr)); - TargetPointer allocAllocated = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapAllocAllocated)); - TargetPointer ephemeralHeapSegment = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapEphemeralHeapSegment)); - TargetPointer cardTable = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable)); - - TargetPointer finalizeQueue = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.GCHeapFinalizeQueue)); - Data.CFinalize finalize = _target.ProcessedData.GetOrAdd(finalizeQueue); - TargetPointer generationTableArrayStart = _target.ReadGlobalPointer(Constants.Globals.GCHeapGenerationTable); - - TargetPointer? savedSweepEphemeralSeg = null; - TargetPointer? savedSweepEphemeralStart = null; - if (_target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralSeg, out TargetPointer? savedSweepEphemeralSegPtr) && - _target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralStart, out TargetPointer? savedSweepEphemeralStartPtr)) - { - savedSweepEphemeralSeg = _target.ReadPointer(savedSweepEphemeralSegPtr.Value); - savedSweepEphemeralStart = _target.ReadPointer(savedSweepEphemeralStartPtr.Value); - } + throw new InvalidOperationException("GetHeapData() is only valid for Workstation GC."); - return new GCHeapData() - { - MarkArray = markArray, - NextSweepObject = nextSweepObj, - BackGroundSavedMinAddress = backgroundMinSavedAddr, - BackGroundSavedMaxAddress = backgroundMaxSavedAddr, - AllocAllocated = allocAllocated, - EphemeralHeapSegment = ephemeralHeapSegment, - CardTable = cardTable, - GenerationTable = GetGenerationData(generationTableArrayStart).AsReadOnly(), - FillPointers = GetFillPointers(finalize).AsReadOnly(), - SavedSweepEphemeralSegment = savedSweepEphemeralSeg ?? TargetPointer.Null, - SavedSweepEphemeralStart = savedSweepEphemeralStart ?? TargetPointer.Null, - }; + return GetGCHeapDataFromHeap(new GCHeapWKS(_target)); } - GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) + GCHeapData IGC.GetHeapData(TargetPointer heapAddress) { if (GetGCType() != GCType.Server) - throw new InvalidOperationException("GetHeapData is only valid for Server GC."); + throw new InvalidOperationException("GetHeapData(TargetPointer heap) is only valid for Server GC."); + + Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); + return GetGCHeapDataFromHeap(heap); + } - Data.GCHeap_svr heap = _target.ProcessedData.GetOrAdd(heapAddress); + private GCHeapData GetGCHeapDataFromHeap(IGCHeap heap) + { Data.CFinalize finalize = _target.ProcessedData.GetOrAdd(heap.FinalizeQueue); return new GCHeapData() @@ -148,6 +153,27 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) FillPointers = GetFillPointers(finalize).AsReadOnly(), SavedSweepEphemeralSegment = heap.SavedSweepEphemeralSeg ?? TargetPointer.Null, SavedSweepEphemeralStart = heap.SavedSweepEphemeralStart ?? TargetPointer.Null, + + InternalRootArray = heap.InternalRootArray, + InternalRootArrayIndex = heap.InternalRootArrayIndex, + HeapAnalyzeSuccess = heap.HeapAnalyzeSuccess, + + InterestingData = ReadGCHeapDataArray( + heap.InterestingData, + _target.ReadGlobal(Constants.Globals.InterestingDataLength)) + .AsReadOnly(), + CompactReasons = ReadGCHeapDataArray( + heap.CompactReasons, + _target.ReadGlobal(Constants.Globals.CompactReasonsLength)) + .AsReadOnly(), + ExpandMechanisms = ReadGCHeapDataArray( + heap.ExpandMechanisms, + _target.ReadGlobal(Constants.Globals.ExpandMechanismsLength)) + .AsReadOnly(), + InterestingMechanismBits = ReadGCHeapDataArray( + heap.InterestingMechanismBits, + _target.ReadGlobal(Constants.Globals.InterestingMechanismBitsLength)) + .AsReadOnly(), }; } @@ -178,10 +204,51 @@ private List GetFillPointers(Data.CFinalize cFinalize) TargetPointer fillPointersArrayStart = cFinalize.FillPointers; List fillPointers = []; for (uint i = 0; i < fillPointersLength; i++) - fillPointers.Add(_target.ReadPointer(fillPointersArrayStart + i * (ulong)_target.PointerSize)); + fillPointers.Add(_target.ReadPointer(fillPointersArrayStart + i * (uint)_target.PointerSize)); return fillPointers; } + private List ReadGCHeapDataArray(TargetPointer arrayStart, uint length) + { + List arr = []; + for (uint i = 0; i < length; i++) + arr.Add(_target.ReadNUInt(arrayStart + (i * (uint)_target.PointerSize))); + return arr; + } + + GCOomData IGC.GetOomData() + { + if (GetGCType() != GCType.Workstation) + throw new InvalidOperationException("GetOomData() is only valid for Workstation GC."); + + TargetPointer oomHistory = _target.ReadGlobalPointer(Constants.Globals.GCHeapOomData); + Data.OomHistory oomHistoryData = _target.ProcessedData.GetOrAdd(oomHistory); + return GetGCOomData(oomHistoryData); + } + + GCOomData IGC.GetOomData(TargetPointer heapAddress) + { + if (GetGCType() != GCType.Server) + throw new InvalidOperationException("GetOomData(TargetPointer heap) is only valid for Server GC."); + + Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); + return GetGCOomData(heap.OomData); + } + + private static GCOomData GetGCOomData(Data.OomHistory oomHistory) + => new GCOomData() + { + Reason = oomHistory.Reason, + AllocSize = oomHistory.AllocSize, + Reserved = oomHistory.Reserved, + Allocated = oomHistory.Allocated, + GCIndex = oomHistory.GcIndex, + Fgm = oomHistory.Fgm, + Size = oomHistory.Size, + AvailablePagefileMB = oomHistory.AvailablePagefileMb, + LohP = oomHistory.LohP != 0, + }; + private GCType GetGCType() { string[] identifiers = ((IGC)this).GetGCIdentifiers(); @@ -204,4 +271,10 @@ private bool IsBackgroundGCEnabled() string[] identifiers = ((IGC)this).GetGCIdentifiers(); return identifiers.Contains(GCIdentifiers.Background); } + + private bool IsDatasEnabled() + { + string[] identifiers = ((IGC)this).GetGCIdentifiers(); + return identifiers.Contains(GCIdentifiers.DynamicHeapCount); + } } 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 new file mode 100644 index 00000000000000..cab7d0ee4d9f1c --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; + +internal interface IGCHeap +{ + TargetPointer MarkArray { get; } + TargetPointer NextSweepObj { get; } + TargetPointer BackgroundMinSavedAddr { get; } + TargetPointer BackgroundMaxSavedAddr { get; } + TargetPointer AllocAllocated { get; } + TargetPointer EphemeralHeapSegment { get; } + TargetPointer CardTable { get; } + TargetPointer FinalizeQueue { get; } + TargetPointer GenerationTable { get; } + + TargetPointer? SavedSweepEphemeralSeg { get; } + TargetPointer? SavedSweepEphemeralStart { get; } + + Data.OomHistory OomData { get; } + + TargetPointer InternalRootArray { get; } + TargetNUInt InternalRootArrayIndex { get; } + bool HeapAnalyzeSuccess { get; } + + TargetPointer InterestingData { get; } + TargetPointer CompactReasons { get; } + TargetPointer ExpandMechanisms { get; } + TargetPointer InterestingMechanismBits { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeap.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs similarity index 59% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeap.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs index 350026322cb287..c3f7520bb4da8c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeap.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/GCHeapSVR.cs @@ -3,13 +3,14 @@ using System; using System.Collections.Generic; +using Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; namespace Microsoft.Diagnostics.DataContractReader.Data; -internal sealed class GCHeap_svr : IData +internal sealed class GCHeapSVR : IData, IGCHeap { - static GCHeap_svr IData.Create(Target target, TargetPointer address) => new GCHeap_svr(target, address); - public GCHeap_svr(Target target, TargetPointer address) + static GCHeapSVR IData.Create(Target target, TargetPointer address) => new GCHeapSVR(target, address); + public GCHeapSVR(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.GCHeap); @@ -28,6 +29,17 @@ public GCHeap_svr(Target target, TargetPointer address) SavedSweepEphemeralSeg = target.ReadPointer(address + (ulong)type.Fields[nameof(SavedSweepEphemeralSeg)].Offset); if (type.Fields.ContainsKey(nameof(SavedSweepEphemeralStart))) SavedSweepEphemeralStart = target.ReadPointer(address + (ulong)type.Fields[nameof(SavedSweepEphemeralStart)].Offset); + + OomData = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(OomData)].Offset); + + InternalRootArray = target.ReadPointer(address + (ulong)type.Fields[nameof(InternalRootArray)].Offset); + InternalRootArrayIndex = target.ReadNUInt(address + (ulong)type.Fields[nameof(InternalRootArrayIndex)].Offset); + HeapAnalyzeSuccess = target.Read(address + (ulong)type.Fields[nameof(HeapAnalyzeSuccess)].Offset) != 0; + + InterestingData = address + (ulong)type.Fields[nameof(InterestingData)].Offset; + CompactReasons = address + (ulong)type.Fields[nameof(CompactReasons)].Offset; + ExpandMechanisms = address + (ulong)type.Fields[nameof(ExpandMechanisms)].Offset; + InterestingMechanismBits = address + (ulong)type.Fields[nameof(InterestingMechanismBits)].Offset; } public TargetPointer MarkArray { get; } @@ -42,4 +54,15 @@ public GCHeap_svr(Target target, TargetPointer address) public TargetPointer? SavedSweepEphemeralSeg { get; } public TargetPointer? SavedSweepEphemeralStart { get; } + + public OomHistory OomData { get; } + + public TargetPointer InternalRootArray { get; } + public TargetNUInt InternalRootArrayIndex { get; } + public bool HeapAnalyzeSuccess { get; } + + public TargetPointer InterestingData { get; } + public TargetPointer CompactReasons { get; } + public TargetPointer ExpandMechanisms { get; } + public TargetPointer InterestingMechanismBits { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/HeapSegment.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/HeapSegment.cs new file mode 100644 index 00000000000000..1ae541d30d6df5 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/HeapSegment.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class HeapSegment : IData +{ + static HeapSegment IData.Create(Target target, TargetPointer address) => new HeapSegment(target, address); + public HeapSegment(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.HeapSegment); + + Allocated = target.ReadPointer(address + (ulong)type.Fields[nameof(Allocated)].Offset); + Committed = target.ReadPointer(address + (ulong)type.Fields[nameof(Committed)].Offset); + Reserved = target.ReadPointer(address + (ulong)type.Fields[nameof(Reserved)].Offset); + Used = target.ReadPointer(address + (ulong)type.Fields[nameof(Used)].Offset); + Mem = target.ReadPointer(address + (ulong)type.Fields[nameof(Mem)].Offset); + Flags = target.ReadNUInt(address + (ulong)type.Fields[nameof(Flags)].Offset); + Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset); + BackgroundAllocated = target.ReadPointer(address + (ulong)type.Fields[nameof(BackgroundAllocated)].Offset); + + // Field only exists in MULTIPLE_HEAPS builds + if (type.Fields.ContainsKey(nameof(Heap))) + Heap = target.ReadPointer(address + (ulong)type.Fields[nameof(Heap)].Offset); + } + + public TargetPointer Allocated { get; } + public TargetPointer Committed { get; } + public TargetPointer Reserved { get; } + public TargetPointer Used { get; } + public TargetPointer Mem { get; } + public TargetNUInt Flags { get; } + public TargetPointer Next { get; } + public TargetPointer BackgroundAllocated { get; } + public TargetPointer? Heap { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/OOMHistory.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/OOMHistory.cs new file mode 100644 index 00000000000000..42f21fc5a589c8 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/OOMHistory.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class OomHistory : IData +{ + static OomHistory IData.Create(Target target, TargetPointer address) => new OomHistory(target, address); + public OomHistory(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.OomHistory); + + Reason = target.Read(address + (ulong)type.Fields[nameof(Reason)].Offset); + AllocSize = target.ReadNUInt(address + (ulong)type.Fields[nameof(AllocSize)].Offset); + Reserved = target.ReadPointer(address + (ulong)type.Fields[nameof(Reserved)].Offset); + Allocated = target.ReadPointer(address + (ulong)type.Fields[nameof(Allocated)].Offset); + GcIndex = target.ReadNUInt(address + (ulong)type.Fields[nameof(GcIndex)].Offset); + Fgm = target.Read(address + (ulong)type.Fields[nameof(Fgm)].Offset); + Size = target.ReadNUInt(address + (ulong)type.Fields[nameof(Size)].Offset); + AvailablePagefileMb = target.ReadNUInt(address + (ulong)type.Fields[nameof(AvailablePagefileMb)].Offset); + LohP = target.Read(address + (ulong)type.Fields[nameof(LohP)].Offset); + } + + public int Reason { get; } + public TargetNUInt AllocSize { get; } + public TargetPointer Reserved { get; } + public TargetPointer Allocated { get; } + public TargetNUInt GcIndex { get; } + public int Fgm { get; } + public TargetNUInt Size { get; } + public TargetNUInt AvailablePagefileMb { get; } + public uint LohP { get; } +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 067c494973b8ba..2ca8ddc2be7b7c 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -280,6 +280,12 @@ internal struct DacpMethodTableTransparencyData internal static class GCConstants { public const int DAC_NUMBERGENERATIONS = 4; + + public const int DAC_NUM_GC_DATA_POINTS = 9; + public const int DAC_MAX_COMPACT_REASONS_COUNT = 11; + public const int DAC_MAX_EXPAND_MECHANISMS_COUNT = 6; + public const int DAC_MAX_GC_MECHANISM_BITS_COUNT = 2; + public const int DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT = 6; } internal struct DacpGcHeapData @@ -334,6 +340,41 @@ public struct FinalizationDataArray public ClrDataAddress card_table; } +internal struct DacpHeapSegmentData +{ + public ClrDataAddress segmentAddr; + public ClrDataAddress allocated; + public ClrDataAddress committed; + public ClrDataAddress reserved; + public ClrDataAddress used; + public ClrDataAddress mem; + public ClrDataAddress next; /* pass this to request if non-null to get the next segments */ + public ClrDataAddress gc_heap; /* only filled in server mode, otherwise NULL */ + public ClrDataAddress highAllocMark; /* if this is the ephemeral segment highMark includes the ephemeral generation */ + + public nuint flags; + public ClrDataAddress background_allocated; +} + +internal struct DacpOomData +{ + public int reason; + public ulong alloc_size; + public ulong available_pagefile_mb; + public ulong gc_index; + public int fgm; + public ulong size; + public int loh_p; // BOOL +} + +internal struct DacpGcHeapAnalyzeData +{ + public ClrDataAddress heapAddr; + public ClrDataAddress internal_root_array; + public ulong internal_root_array_index; + public int heap_analyze_success; // BOOL +} + [GeneratedComInterface] [Guid("286CA186-E763-4F61-9760-487D43AE4341")] internal unsafe partial interface ISOSEnum @@ -479,15 +520,15 @@ internal unsafe partial interface ISOSDacInterface [PreserveSig] int GetGCHeapStaticData(DacpGcHeapDetails* details); [PreserveSig] - int GetHeapSegmentData(ClrDataAddress seg, /*struct DacpHeapSegmentData */ void* data); + int GetHeapSegmentData(ClrDataAddress seg, DacpHeapSegmentData* data); [PreserveSig] - int GetOOMData(ClrDataAddress oomAddr, /*struct DacpOomData */ void* data); + int GetOOMData(ClrDataAddress oomAddr, DacpOomData* data); [PreserveSig] - int GetOOMStaticData(/*struct DacpOomData */ void* data); + int GetOOMStaticData(DacpOomData* data); [PreserveSig] - int GetHeapAnalyzeData(ClrDataAddress addr, /*struct DacpGcHeapAnalyzeData */ void* data); + int GetHeapAnalyzeData(ClrDataAddress addr, DacpGcHeapAnalyzeData* data); [PreserveSig] - int GetHeapAnalyzeStaticData(/*struct DacpGcHeapAnalyzeData */ void* data); + int GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data); // DomainLocal [PreserveSig] @@ -613,14 +654,53 @@ internal unsafe partial interface ISOSDacInterface2 int IsRCWDCOMProxy(ClrDataAddress rcwAddress, int* inDCOMProxy); } +internal struct DacpGCInterestingInfoData +{ + [System.Runtime.CompilerServices.InlineArray(GCConstants.DAC_NUM_GC_DATA_POINTS)] + public struct InterestingDataPointsArray + { + private nuint _; + } + public InterestingDataPointsArray interestingDataPoints; + + + [System.Runtime.CompilerServices.InlineArray(GCConstants.DAC_MAX_COMPACT_REASONS_COUNT)] + public struct CompactReasonsArray + { + private nuint _; + } + public CompactReasonsArray compactReasons; + + [System.Runtime.CompilerServices.InlineArray(GCConstants.DAC_MAX_EXPAND_MECHANISMS_COUNT)] + public struct ExpandMechanismsArray + { + private nuint _; + } + public ExpandMechanismsArray expandMechanisms; + + [System.Runtime.CompilerServices.InlineArray(GCConstants.DAC_MAX_GC_MECHANISM_BITS_COUNT)] + public struct BitMechanismsArray + { + private nuint _; + } + public BitMechanismsArray bitMechanisms; + + [System.Runtime.CompilerServices.InlineArray(GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT)] + public struct GlobalMechanismsArray + { + private nuint _; + } + public GlobalMechanismsArray globalMechanisms; +} + [GeneratedComInterface] [Guid("B08C5CDC-FD8A-49C5-AB38-5FEEF35235B4")] internal unsafe partial interface ISOSDacInterface3 { [PreserveSig] - int GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data); + int GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, DacpGCInterestingInfoData* data); [PreserveSig] - int GetGCInterestingInfoStaticData(/*struct DacpGCInterestingInfoData*/ void* data); + int GetGCInterestingInfoStaticData(DacpGCInterestingInfoData* data); [PreserveSig] int GetGCGlobalMechanisms(nuint* globalMechanisms); }; diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 157406337bbd22..a5ac1b7025a1a7 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -934,7 +934,7 @@ int ISOSDacInterface.GetGCHeapDetails(ClrDataAddress heap, DacpGcHeapDetails* de if (!gcIdentifiers.Contains(GCIdentifiers.Server)) throw new ArgumentException(); - GCHeapData heapData = gc.SVRGetHeapData(heap.ToTargetPointer(_target)); + GCHeapData heapData = gc.GetHeapData(heap.ToTargetPointer(_target)); details->heapAddr = heap; @@ -1067,7 +1067,7 @@ int ISOSDacInterface.GetGCHeapStaticData(DacpGcHeapDetails* details) if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) throw new ArgumentException(); - GCHeapData heapData = gc.WKSGetHeapData(); + GCHeapData heapData = gc.GetHeapData(); details->heapAddr = 0; @@ -1192,12 +1192,165 @@ int ISOSDacInterface.GetHandleEnumForTypes([In, MarshalUsing(CountElementName = => _legacyImpl is not null ? _legacyImpl.GetHandleEnumForTypes(types, count, ppHandleEnum) : HResults.E_NOTIMPL; int ISOSDacInterface.GetHeapAllocData(uint count, void* data, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetHeapAllocData(count, data, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, void* data) - => _legacyImpl is not null ? _legacyImpl.GetHeapAnalyzeData(addr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHeapAnalyzeStaticData(void* data) - => _legacyImpl is not null ? _legacyImpl.GetHeapAnalyzeStaticData(data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, void* data) - => _legacyImpl is not null ? _legacyImpl.GetHeapSegmentData(seg, data) : HResults.E_NOTIMPL; + int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, DacpGcHeapAnalyzeData* data) + { + int hr = HResults.S_OK; + try + { + if (addr == 0 || data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // doesn't make sense to call this on WKS mode + if (!gcIdentifiers.Contains(GCIdentifiers.Server)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + GCHeapData heapData = gc.GetHeapData(addr.ToTargetPointer(_target)); + + data->heapAddr = addr; + data->internal_root_array = heapData.InternalRootArray.ToClrDataAddress(_target); + data->internal_root_array_index = heapData.InternalRootArrayIndex.Value; + data->heap_analyze_success = heapData.HeapAnalyzeSuccess ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + DacpGcHeapAnalyzeData dataLocal = default; + int hrLocal = _legacyImpl.GetHeapAnalyzeData(addr, &dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->heapAddr == dataLocal.heapAddr, $"cDAC: {data->heapAddr:x}, DAC: {dataLocal.heapAddr:x}"); + Debug.Assert(data->internal_root_array == dataLocal.internal_root_array, $"cDAC: {data->internal_root_array:x}, DAC: {dataLocal.internal_root_array:x}"); + Debug.Assert(data->internal_root_array_index == dataLocal.internal_root_array_index, $"cDAC: {data->internal_root_array_index}, DAC: {dataLocal.internal_root_array_index}"); + Debug.Assert(data->heap_analyze_success == dataLocal.heap_analyze_success, $"cDAC: {data->heap_analyze_success}, DAC: {dataLocal.heap_analyze_success}"); + } + } +#endif + + return hr; + } + int ISOSDacInterface.GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data) + { + int hr = HResults.S_OK; + try + { + if (data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // doesn't make sense to call this on SVR mode + if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + // For workstation GC, use GetHeapData() + GCHeapData heapData = gc.GetHeapData(); + + data->heapAddr = 0; // Not applicable for static data + data->internal_root_array = heapData.InternalRootArray.ToClrDataAddress(_target); + data->internal_root_array_index = heapData.InternalRootArrayIndex.Value; + data->heap_analyze_success = heapData.HeapAnalyzeSuccess ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + DacpGcHeapAnalyzeData dataLocal = default; + int hrLocal = _legacyImpl.GetHeapAnalyzeStaticData(&dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->heapAddr == dataLocal.heapAddr, $"cDAC: {data->heapAddr:x}, DAC: {dataLocal.heapAddr:x}"); + Debug.Assert(data->internal_root_array == dataLocal.internal_root_array, $"cDAC: {data->internal_root_array:x}, DAC: {dataLocal.internal_root_array:x}"); + Debug.Assert(data->internal_root_array_index == dataLocal.internal_root_array_index, $"cDAC: {data->internal_root_array_index}, DAC: {dataLocal.internal_root_array_index}"); + Debug.Assert(data->heap_analyze_success == dataLocal.heap_analyze_success, $"cDAC: {data->heap_analyze_success}, DAC: {dataLocal.heap_analyze_success}"); + } + } +#endif + + return hr; + } + int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, DacpHeapSegmentData* data) + { + int hr = HResults.S_OK; + try + { + if (seg == 0 || data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + GCHeapSegmentData segmentData = gc.GetHeapSegmentData(seg.ToTargetPointer(_target)); + + data->segmentAddr = seg; + data->allocated = segmentData.Allocated.ToClrDataAddress(_target); + data->committed = segmentData.Committed.ToClrDataAddress(_target); + data->reserved = segmentData.Reserved.ToClrDataAddress(_target); + data->used = segmentData.Used.ToClrDataAddress(_target); + data->mem = segmentData.Mem.ToClrDataAddress(_target); + data->next = segmentData.Next.ToClrDataAddress(_target); + data->gc_heap = segmentData.Heap.ToClrDataAddress(_target); + data->flags = (nuint)segmentData.Flags.Value; + data->background_allocated = segmentData.BackgroundAllocated.ToClrDataAddress(_target); + + // TODO: Compute highAllocMark - need to determine if this is the ephemeral segment + // and get the allocation mark from the appropriate heap data + // For now, use allocated as a fallback (similar to non-ephemeral segments in legacy code) + data->highAllocMark = data->allocated; + + GCHeapData heapData = gcIdentifiers.Contains(GCIdentifiers.Server) ? gc.GetHeapData(segmentData.Heap) : gc.GetHeapData(); + if (seg.ToTargetPointer(_target) == heapData.EphemeralHeapSegment) + { + data->highAllocMark = heapData.AllocAllocated.ToClrDataAddress(_target); + } + else + { + data->highAllocMark = data->allocated; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + DacpHeapSegmentData dataLocal = default; + int hrLocal = _legacyImpl.GetHeapSegmentData(seg, &dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->segmentAddr == dataLocal.segmentAddr, $"segmentAddr - cDAC: {data->segmentAddr:x}, DAC: {dataLocal.segmentAddr:x}"); + Debug.Assert(data->allocated == dataLocal.allocated, $"allocated - cDAC: {data->allocated:x}, DAC: {dataLocal.allocated:x}"); + Debug.Assert(data->committed == dataLocal.committed, $"committed - cDAC: {data->committed:x}, DAC: {dataLocal.committed:x}"); + Debug.Assert(data->reserved == dataLocal.reserved, $"reserved - cDAC: {data->reserved:x}, DAC: {dataLocal.reserved:x}"); + Debug.Assert(data->used == dataLocal.used, $"used - cDAC: {data->used:x}, DAC: {dataLocal.used:x}"); + Debug.Assert(data->mem == dataLocal.mem, $"mem - cDAC: {data->mem:x}, DAC: {dataLocal.mem:x}"); + Debug.Assert(data->next == dataLocal.next, $"next - cDAC: {data->next:x}, DAC: {dataLocal.next:x}"); + Debug.Assert(data->gc_heap == dataLocal.gc_heap, $"gc_heap - cDAC: {data->gc_heap:x}, DAC: {dataLocal.gc_heap:x}"); + Debug.Assert(data->highAllocMark == dataLocal.highAllocMark, $"highAllocMark - cDAC: {data->highAllocMark:x}, DAC: {dataLocal.highAllocMark:x}"); + Debug.Assert(data->flags == dataLocal.flags, $"flags - cDAC: {data->flags:x}, DAC: {dataLocal.flags:x}"); + Debug.Assert(data->background_allocated == dataLocal.background_allocated, $"background_allocated - cDAC: {data->background_allocated:x}, DAC: {dataLocal.background_allocated:x}"); + } + } +#endif + + return hr; + } int ISOSDacInterface.GetHillClimbingLogEntry(ClrDataAddress addr, void* data) { @@ -2478,10 +2631,106 @@ int ISOSDacInterface.GetObjectStringData(ClrDataAddress obj, uint count, char* s return hr; } - int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, void* data) - => _legacyImpl is not null ? _legacyImpl.GetOOMData(oomAddr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetOOMStaticData(void* data) - => _legacyImpl is not null ? _legacyImpl.GetOOMStaticData(data) : HResults.E_NOTIMPL; + int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, DacpOomData* data) + { + int hr = HResults.S_OK; + try + { + if (oomAddr == 0 || data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // This method is only valid for server GC mode + if (!gcIdentifiers.Contains(GCIdentifiers.Server)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + GCOomData oomData = gc.GetOomData(oomAddr.ToTargetPointer(_target)); + + data->reason = oomData.Reason; + data->alloc_size = oomData.AllocSize.Value; + data->available_pagefile_mb = oomData.AvailablePagefileMB.Value; + data->gc_index = oomData.GCIndex.Value; + data->fgm = oomData.Fgm; + data->size = oomData.Size.Value; + data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + DacpOomData dataLocal; + int hrLocal = _legacyImpl.GetOOMData(oomAddr, &dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->reason == dataLocal.reason, $"cDAC: {data->reason}, DAC: {dataLocal.reason}"); + Debug.Assert(data->alloc_size == dataLocal.alloc_size, $"cDAC: {data->alloc_size}, DAC: {dataLocal.alloc_size}"); + Debug.Assert(data->available_pagefile_mb == dataLocal.available_pagefile_mb, $"cDAC: {data->available_pagefile_mb}, DAC: {dataLocal.available_pagefile_mb}"); + Debug.Assert(data->gc_index == dataLocal.gc_index, $"cDAC: {data->gc_index}, DAC: {dataLocal.gc_index}"); + Debug.Assert(data->fgm == dataLocal.fgm, $"cDAC: {data->fgm}, DAC: {dataLocal.fgm}"); + Debug.Assert(data->size == dataLocal.size, $"cDAC: {data->size}, DAC: {dataLocal.size}"); + Debug.Assert(data->loh_p == dataLocal.loh_p, $"cDAC: {data->loh_p}, DAC: {dataLocal.loh_p}"); + } + } +#endif + return hr; + } + + int ISOSDacInterface.GetOOMStaticData(DacpOomData* data) + { + int hr = HResults.S_OK; + try + { + if (data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // This method is only valid for workstation GC mode + if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + GCOomData oomData = gc.GetOomData(); + + data->reason = oomData.Reason; + data->alloc_size = oomData.AllocSize.Value; + data->available_pagefile_mb = oomData.AvailablePagefileMB.Value; + data->gc_index = oomData.GCIndex.Value; + data->fgm = oomData.Fgm; + data->size = oomData.Size.Value; + data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + DacpOomData dataLocal; + int hrLocal = _legacyImpl.GetOOMStaticData(&dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->reason == dataLocal.reason, $"cDAC: {data->reason}, DAC: {dataLocal.reason}"); + Debug.Assert(data->alloc_size == dataLocal.alloc_size, $"cDAC: {data->alloc_size}, DAC: {dataLocal.alloc_size}"); + Debug.Assert(data->available_pagefile_mb == dataLocal.available_pagefile_mb, $"cDAC: {data->available_pagefile_mb}, DAC: {dataLocal.available_pagefile_mb}"); + Debug.Assert(data->gc_index == dataLocal.gc_index, $"cDAC: {data->gc_index}, DAC: {dataLocal.gc_index}"); + Debug.Assert(data->fgm == dataLocal.fgm, $"cDAC: {data->fgm}, DAC: {dataLocal.fgm}"); + Debug.Assert(data->size == dataLocal.size, $"cDAC: {data->size}, DAC: {dataLocal.size}"); + Debug.Assert(data->loh_p == dataLocal.loh_p, $"cDAC: {data->loh_p}, DAC: {dataLocal.loh_p}"); + } + } +#endif + return hr; + } int ISOSDacInterface.GetPEFileBase(ClrDataAddress addr, ClrDataAddress* peBase) { @@ -3046,12 +3295,206 @@ int ISOSDacInterface2.IsRCWDCOMProxy(ClrDataAddress rcwAddress, int* inDCOMProxy #endregion ISOSDacInterface2 #region ISOSDacInterface3 - int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, /*struct DacpGCInterestingInfoData*/ void* data) - => _legacyImpl3 is not null ? _legacyImpl3.GetGCInterestingInfoData(interestingInfoAddr, data) : HResults.E_NOTIMPL; - int ISOSDacInterface3.GetGCInterestingInfoStaticData(/*struct DacpGCInterestingInfoData*/ void* data) - => _legacyImpl3 is not null ? _legacyImpl3.GetGCInterestingInfoStaticData(data) : HResults.E_NOTIMPL; + int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, DacpGCInterestingInfoData* data) + { + int hr = HResults.S_OK; + + try + { + if (interestingInfoAddr == 0 || data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // doesn't make sense to call this on WKS mode + if (!gcIdentifiers.Contains(GCIdentifiers.Server)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + // For server GC, use GetHeapData(TargetPointer heap) + GCHeapData heapData = gc.GetHeapData(interestingInfoAddr.ToTargetPointer(_target)); + + PopulateGCInterestingInfoData(heapData, data); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl3 is not null) + { + DacpGCInterestingInfoData dataLocal = default; + int hrLocal = _legacyImpl3.GetGCInterestingInfoData(interestingInfoAddr, &dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + VerifyGCInterestingInfoData(data, &dataLocal); + } + } +#endif + + return hr; + } + + int ISOSDacInterface3.GetGCInterestingInfoStaticData(DacpGCInterestingInfoData* data) + { + int hr = HResults.S_OK; + + try + { + if (data == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + string[] gcIdentifiers = gc.GetGCIdentifiers(); + + // doesn't make sense to call this on SVR mode + if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) + throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; + + // For workstation GC, use GetHeapData() + GCHeapData heapData = gc.GetHeapData(); + + PopulateGCInterestingInfoData(heapData, data); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl3 is not null) + { + DacpGCInterestingInfoData dataLocal = default; + int hrLocal = _legacyImpl3.GetGCInterestingInfoStaticData(&dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + VerifyGCInterestingInfoData(data, &dataLocal); + } + } +#endif + + return hr; + } + + private static void PopulateGCInterestingInfoData(GCHeapData heapData, DacpGCInterestingInfoData* data) + { + *data = default; + + // The DacpGCInterestingInfoData struct hardcodes platform sized ints. + // This is problematic for new cross-bit scenarios. + // If the target platform is 64-bits but the cDAC/SOS is 32-bits, the values will be truncated. + + // Copy interesting data points + for (int i = 0; i < Math.Min(GCConstants.DAC_NUM_GC_DATA_POINTS, heapData.InterestingData.Count); i++) + data->interestingDataPoints[i] = (nuint)heapData.InterestingData[i].Value; + + // Copy compact reasons + for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_COMPACT_REASONS_COUNT, heapData.CompactReasons.Count); i++) + data->compactReasons[i] = (nuint)heapData.CompactReasons[i].Value; + + // Copy expand mechanisms + for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_EXPAND_MECHANISMS_COUNT, heapData.ExpandMechanisms.Count); i++) + data->expandMechanisms[i] = (nuint)heapData.ExpandMechanisms[i].Value; + + // Copy interesting mechanism bits + for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_GC_MECHANISM_BITS_COUNT, heapData.InterestingMechanismBits.Count); i++) + data->bitMechanisms[i] = (nuint)heapData.InterestingMechanismBits[i].Value; + } + +#if DEBUG + private static void VerifyGCInterestingInfoData(DacpGCInterestingInfoData* cdacData, DacpGCInterestingInfoData* legacyData) + { + // Compare interesting data points array + for (int i = 0; i < GCConstants.DAC_NUM_GC_DATA_POINTS; i++) + { + Debug.Assert(cdacData->interestingDataPoints[i] == legacyData->interestingDataPoints[i], + $"interestingDataPoints[{i}] - cDAC: {cdacData->interestingDataPoints[i]}, DAC: {legacyData->interestingDataPoints[i]}"); + } + + // Compare compact reasons array + for (int i = 0; i < GCConstants.DAC_MAX_COMPACT_REASONS_COUNT; i++) + { + Debug.Assert(cdacData->compactReasons[i] == legacyData->compactReasons[i], + $"compactReasons[{i}] - cDAC: {cdacData->compactReasons[i]}, DAC: {legacyData->compactReasons[i]}"); + } + + // Compare expand mechanisms array + for (int i = 0; i < GCConstants.DAC_MAX_EXPAND_MECHANISMS_COUNT; i++) + { + Debug.Assert(cdacData->expandMechanisms[i] == legacyData->expandMechanisms[i], + $"expandMechanisms[{i}] - cDAC: {cdacData->expandMechanisms[i]}, DAC: {legacyData->expandMechanisms[i]}"); + } + + // Compare bit mechanisms array + for (int i = 0; i < GCConstants.DAC_MAX_GC_MECHANISM_BITS_COUNT; i++) + { + Debug.Assert(cdacData->bitMechanisms[i] == legacyData->bitMechanisms[i], + $"bitMechanisms[{i}] - cDAC: {cdacData->bitMechanisms[i]}, DAC: {legacyData->bitMechanisms[i]}"); + } + + // Compare global mechanisms array + for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) + { + Debug.Assert(cdacData->globalMechanisms[i] == legacyData->globalMechanisms[i], + $"globalMechanisms[{i}] - cDAC: {cdacData->globalMechanisms[i]}, DAC: {legacyData->globalMechanisms[i]}"); + } + } +#endif + int ISOSDacInterface3.GetGCGlobalMechanisms(nuint* globalMechanisms) - => _legacyImpl3 is not null ? _legacyImpl3.GetGCGlobalMechanisms(globalMechanisms) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (globalMechanisms == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + IReadOnlyList globalMechanismsData = gc.GetGlobalMechanisms(); + + // Clear the array + for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) + globalMechanisms[i] = 0; + + // Copy global mechanisms data + for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT, globalMechanismsData.Count); i++) + { + // This API hardcodes platform sized ints in the struct + // This is problematic for new cross-bit scenarios. + // If the target platform is 64-bits but the cDAC/SOS is 32-bits, the values will be truncated. + globalMechanisms[i] = (nuint)globalMechanismsData[i].Value; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl3 is not null) + { + nuint[] globalMechanismsLocal = new nuint[GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT]; + fixed (nuint* pLocal = globalMechanismsLocal) + { + int hrLocal = _legacyImpl3.GetGCGlobalMechanisms(pLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) + { + Debug.Assert(globalMechanisms[i] == globalMechanismsLocal[i], + $"globalMechanisms[{i}] - cDAC: {globalMechanisms[i]}, DAC: {globalMechanismsLocal[i]}"); + } + } + } + } +#endif + + return hr; + } #endregion ISOSDacInterface3 #region ISOSDacInterface4 @@ -3814,6 +4257,43 @@ int ISOSDacInterface15.GetMethodTableSlotEnumerator(ClrDataAddress mt, out ISOSM #region ISOSDacInterface16 int ISOSDacInterface16.GetGCDynamicAdaptationMode(int* pDynamicAdaptationMode) - => _legacyImpl16 is not null ? _legacyImpl16.GetGCDynamicAdaptationMode(pDynamicAdaptationMode) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + + try + { + if (pDynamicAdaptationMode == null) + throw new ArgumentException(); + + IGC gc = _target.Contracts.GC; + if (gc.TryGetGCDynamicAdaptationMode(out int mode)) + { + *pDynamicAdaptationMode = mode; + } + else + { + *pDynamicAdaptationMode = -1; + hr = HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl16 is not null) + { + int dynamicAdaptationModeLocal; + int hrLocal = _legacyImpl16.GetGCDynamicAdaptationMode(&dynamicAdaptationModeLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK || hr == HResults.S_FALSE) + { + Debug.Assert(pDynamicAdaptationMode == null || *pDynamicAdaptationMode == dynamicAdaptationModeLocal); + } + } +#endif + return hr; + } #endregion ISOSDacInterface16 }