From 338ba7971d2cade678b8d049729e4a8827ba3d4c Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 3 Sep 2025 12:12:14 -0400 Subject: [PATCH 01/14] ISOSDacInterface16::GetGCDynamicAdaptationMode --- docs/design/datacontracts/GC.md | 21 +++++++++++++++-- .../gc/datadescriptor/datadescriptor.h | 3 +++ .../gc/datadescriptor/datadescriptor.inc | 23 ++++++++++++++----- .../Contracts/IGC.cs | 3 +++ .../Constants.cs | 1 + .../Contracts/GC_1.cs | 19 +++++++++++++-- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 01562fafebafd5..d266498b0ec452 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -39,6 +39,7 @@ public readonly struct GCGenerationData // "workstation" or "server" // "segments" or "regions" // "background" + // "dynamic_heap" string[] GetGCIdentifiers(); // Return the number of GC heaps @@ -51,6 +52,8 @@ public readonly struct GCGenerationData void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr); // Gets the current GC state enum value uint GetCurrentGCState(); + // Gets the current GC heap dynamic adaptation mode. -1 if not enabled. + int GetDynamicAdaptationMode(); // Returns pointers to all GC heaps. IEnumerable GetGCHeaps(); @@ -105,6 +108,8 @@ 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) | +| `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 +128,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 +170,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 +178,17 @@ uint IGC.GetCurrentGCState() return 0; } +int IGC.GetDynamicAdaptationMode() +{ + string[] gcIdentifiers = GetGCIdentifiers(); + if (gcIdentifiers.Contains("dynamic_heap)) + { + return target.read(target.ReadGlobalPointer("DynamicAdaptationMode")); + } + + return -1; +} + IEnumerable IGC.GetGCHeaps() { string[] gcIdentifiers = GetGCIdentifiers(); diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index d21cccb71d8956..adfa2a068fa620 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -41,6 +41,9 @@ 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 diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index 343182ef0822af..be8a96ab481e71 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -78,15 +78,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, &GC_NAMESPACE::gc_heap::dynamic_adaptation_mode) +#endif // DYNAMIC_HEAP_COUNT + 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..7b405c0ae3abb4 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 @@ -55,6 +57,7 @@ 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(); + int GetDynamicAdaptationMode() => throw new NotImplementedException(); IEnumerable GetGCHeaps() => throw new NotImplementedException(); /* WKS only APIs */ 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 3ea7cdfa963a2f..bc5f892997dd09 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -85,6 +85,7 @@ 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 DynamicAdaptationMode = nameof(DynamicAdaptationMode); public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength); public const string TotalGenerationCount = nameof(TotalGenerationCount); 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_1.cs index 08183448c0c32d..da0381112a2e85 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_1.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -29,7 +28,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 +71,16 @@ uint IGC.GetCurrentGCState() return _target.Read(_target.ReadGlobalPointer(Constants.Globals.CurrentGCState)); } + int IGC.GetDynamicAdaptationMode() + { + // not enabled = -1 + // dynamic_adaptation_default = 0, + // dynamic_adaptation_to_application_sizes = 1, + if (!IsDatasEnabled()) + return -1; + return _target.Read(_target.ReadGlobalPointer(Constants.Globals.DynamicAdaptationMode)); + } + IEnumerable IGC.GetGCHeaps() { if (GetGCType() != GCType.Server) @@ -204,4 +213,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); + } } From 40cd236783dba42215c0ab9d6c2110891799e1af Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:54:23 -0400 Subject: [PATCH 02/14] more GC api work --- .../gc/datadescriptor/datadescriptor.h | 2 + .../gc/datadescriptor/datadescriptor.inc | 32 ++++- .../Contracts/IGC.cs | 29 ++++ .../DataType.cs | 2 + .../Constants.cs | 1 + .../Contracts/GC_1.cs | 50 +++++++ .../Data/GC/GCHeap.cs | 4 + .../Data/GC/HeapSegment.cs | 36 +++++ .../Data/GC/OOMHistory.cs | 33 +++++ .../Legacy/ISOSDacInterface.cs | 15 +- .../Legacy/SOSDacImpl.cs | 135 +++++++++++++++++- 11 files changed, 331 insertions(+), 8 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/HeapSegment.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/OOMHistory.cs diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index adfa2a068fa620..b1acda447d6f75 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -48,6 +48,8 @@ struct cdac_data static constexpr GC_NAMESPACE::gc_heap*** Heaps = &GC_NAMESPACE::gc_heap::g_heaps; #endif // SERVER_GC + GC_HEAP_FIELD(OOMData, oom_info) + GC_HEAP_FIELD(MarkArray, mark_array) GC_HEAP_FIELD(NextSweepObj, next_sweep_obj) GC_HEAP_FIELD(BackgroundMinSavedAddr, background_saved_lowest_address) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index be8a96ab481e71..156b53d4c448c3 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -26,6 +26,7 @@ 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_END(GCHeap) #endif // SERVER_GC @@ -43,6 +44,34 @@ 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() @@ -64,6 +93,7 @@ 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) #endif // !SERVER_GC #ifdef SERVER_GC @@ -108,7 +138,7 @@ CDAC_GLOBAL_POINTER(CurrentGCState, cdac_data::CurrentGCS #endif // BACKGROUND_GC #ifdef DYNAMIC_HEAP_COUNT -CDAC_GLOBAL_POINTER(DynamicAdaptationMode, &GC_NAMESPACE::gc_heap::dynamic_adaptation_mode) +CDAC_GLOBAL_POINTER(DynamicAdaptationMode, cdac_data::DynamicAdaptationMode) #endif // DYNAMIC_HEAP_COUNT 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 7b405c0ae3abb4..2eba33c387daaa 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 @@ -46,6 +46,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); @@ -58,13 +84,16 @@ public interface IGC : IContract void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr) => throw new NotImplementedException(); uint GetCurrentGCState() => throw new NotImplementedException(); int GetDynamicAdaptationMode() => throw new NotImplementedException(); + GCHeapSegmentData GetHeapSegmentData(TargetPointer segmentAddress) => throw new NotImplementedException(); IEnumerable GetGCHeaps() => throw new NotImplementedException(); /* WKS only APIs */ GCHeapData WKSGetHeapData() => throw new NotImplementedException(); + GCOOMData WKSGetOOMData() => throw new NotImplementedException(); /* SVR only APIs */ GCHeapData SVRGetHeapData(TargetPointer heapAddress) => throw new NotImplementedException(); + GCOOMData SVRGetOOMData(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 5dcb0078bf9854..0911d974d0ac0d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -138,4 +138,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 bc5f892997dd09..f48ad66a6e0935 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -100,6 +100,7 @@ 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 static class FieldNames { 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_1.cs index da0381112a2e85..ef6fbf6e852135 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_1.cs @@ -81,6 +81,23 @@ int IGC.GetDynamicAdaptationMode() return _target.Read(_target.ReadGlobalPointer(Constants.Globals.DynamicAdaptationMode)); } + 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, + }; + } + IEnumerable IGC.GetGCHeaps() { if (GetGCType() != GCType.Server) @@ -160,6 +177,39 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) }; } + GCOOMData IGC.WKSGetOOMData() + { + if (GetGCType() != GCType.Workstation) + throw new InvalidOperationException("WKSGetHeapData 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.SVRGetOOMData(TargetPointer heapAddress) + { + if (GetGCType() != GCType.Server) + throw new InvalidOperationException("GetHeapData is only valid for Server GC."); + + Data.GCHeap_svr 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 List GetGenerationData(TargetPointer generationTableArrayStart) { uint generationTableLength = _target.ReadGlobal(Constants.Globals.TotalGenerationCount); 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/GCHeap.cs index 350026322cb287..34f12b2c7b2e10 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/GCHeap.cs @@ -28,6 +28,8 @@ 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); } public TargetPointer MarkArray { get; } @@ -42,4 +44,6 @@ public GCHeap_svr(Target target, TargetPointer address) public TargetPointer? SavedSweepEphemeralSeg { get; } public TargetPointer? SavedSweepEphemeralStart { get; } + + public OOMHistory OOMData { 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..29b9d2b8314153 --- /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 a216d02279f2f9..205ee2fbce8fe7 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -335,6 +335,17 @@ public struct FinalizationDataArray public ClrDataAddress card_table; } +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 -> int +} + [GeneratedComInterface] [Guid("436f00f2-b42a-4b9f-870c-e73db66ae930")] internal unsafe partial interface ISOSDacInterface @@ -468,9 +479,9 @@ internal unsafe partial interface ISOSDacInterface [PreserveSig] int GetHeapSegmentData(ClrDataAddress seg, /*struct DacpHeapSegmentData */ void* 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); [PreserveSig] diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 4514d4526c8744..ed8f402bb38a32 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -2326,10 +2326,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.SVRGetOOMData(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 ? 1 : 0; + } + 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.WKSGetOOMData(); + + 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 ? 1 : 0; + } + 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) { @@ -3230,6 +3326,35 @@ int ISOSDacInterface15.GetMethodTableSlotEnumerator(ClrDataAddress mt, /*ISOSMet #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; + *pDynamicAdaptationMode = gc.GetDynamicAdaptationMode(); + } + 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 && pDynamicAdaptationMode != null) + { + Debug.Assert(*pDynamicAdaptationMode == dynamicAdaptationModeLocal); + } + } +#endif + return hr; + } #endregion ISOSDacInterface16 } From 2e9cbb15acc3a82f4dae0379f2b3e1e04158853f Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 4 Sep 2025 13:20:38 -0400 Subject: [PATCH 03/14] HeapAnalyze --- .../gc/datadescriptor/datadescriptor.h | 14 ++- .../gc/datadescriptor/datadescriptor.inc | 25 ++++ .../Contracts/IGC.cs | 10 +- .../Constants.cs | 7 ++ .../Contracts/{ => GC}/GCFactory.cs | 0 .../Contracts/GC/GCHeapWKS.cs | 60 +++++++++ .../Contracts/{ => GC}/GC_1.cs | 117 ++++++++---------- .../Contracts/GC/IGCHeap.cs | 31 +++++ .../Data/GC/{GCHeap.cs => GCHeapSVR.cs} | 25 +++- .../Legacy/ISOSDacInterface.cs | 63 +++++++++- .../Legacy/SOSDacImpl.cs | 105 +++++++++++++++- 11 files changed, 368 insertions(+), 89 deletions(-) rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/{ => GC}/GCFactory.cs (100%) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GCHeapWKS.cs rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/{ => GC}/GC_1.cs (75%) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/IGCHeap.cs rename src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/GC/{GCHeap.cs => GCHeapSVR.cs} (64%) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index b1acda447d6f75..42efeac7044408 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -50,6 +50,7 @@ struct cdac_data 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) @@ -58,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 156b53d4c448c3..62fd739189079f 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -27,6 +27,13 @@ CDAC_TYPE_FIELD(GCHeap, /*pointer*/, SavedSweepEphemeralSeg, 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 @@ -78,6 +85,12 @@ 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) @@ -94,6 +107,14 @@ CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralSeg, 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 @@ -141,4 +162,8 @@ CDAC_GLOBAL_POINTER(CurrentGCState, cdac_data::CurrentGCS 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_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 2eba33c387daaa..079c2e7198eb07 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 @@ -25,17 +25,17 @@ 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 readonly struct GCGenerationData 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 f48ad66a6e0935..e352b1145a0a8d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -101,6 +101,13 @@ public static class Globals 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..a41ac1e4f55535 --- /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.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 75% 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 ef6fbf6e852135..8627e1c2ab1fad 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 @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -116,41 +117,7 @@ GCHeapData IGC.WKSGetHeapData() 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); - } - - 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) @@ -158,7 +125,12 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) if (GetGCType() != GCType.Server) throw new InvalidOperationException("GetHeapData is only valid for Server GC."); - Data.GCHeap_svr heap = _target.ProcessedData.GetOrAdd(heapAddress); + Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); + return GetGCHeapDataFromHeap(heap); + } + + private GCHeapData GetGCHeapDataFromHeap(IGCHeap heap) + { Data.CFinalize finalize = _target.ProcessedData.GetOrAdd(heap.FinalizeQueue); return new GCHeapData() @@ -174,9 +146,44 @@ 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, }; } + private List GetGenerationData(TargetPointer generationTableArrayStart) + { + uint generationTableLength = _target.ReadGlobal(Constants.Globals.TotalGenerationCount); + uint generationSize = _target.GetTypeInfo(DataType.Generation).Size ?? throw new InvalidOperationException("Type Generation has no size"); + List generationTable = []; + for (uint i = 0; i < generationTableLength; i++) + { + TargetPointer generationAddress = generationTableArrayStart + i * generationSize; + generationTable.Add(_target.ProcessedData.GetOrAdd(generationAddress)); + } + List generationDataList = generationTable.Select(gen => + new GCGenerationData() + { + StartSegment = gen.StartSegment, + AllocationStart = gen.AllocationStart ?? 0, + AllocationContextPointer = gen.AllocationContext.Pointer, + AllocationContextLimit = gen.AllocationContext.Limit, + }).ToList(); + return generationDataList; + } + + private List GetFillPointers(Data.CFinalize cFinalize) + { + uint fillPointersLength = _target.ReadGlobal(Constants.Globals.CFinalizeFillPointersLength); + TargetPointer fillPointersArrayStart = cFinalize.FillPointers; + List fillPointers = []; + for (uint i = 0; i < fillPointersLength; i++) + fillPointers.Add(_target.ReadPointer(fillPointersArrayStart + i * (ulong)_target.PointerSize)); + return fillPointers; + } + GCOOMData IGC.WKSGetOOMData() { if (GetGCType() != GCType.Workstation) @@ -192,7 +199,7 @@ GCOOMData IGC.SVRGetOOMData(TargetPointer heapAddress) if (GetGCType() != GCType.Server) throw new InvalidOperationException("GetHeapData is only valid for Server GC."); - Data.GCHeap_svr heap = _target.ProcessedData.GetOrAdd(heapAddress); + Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); return GetGCOOMData(heap.OOMData); } @@ -210,37 +217,6 @@ private static GCOOMData GetGCOOMData(Data.OOMHistory oomHistory) LohP = oomHistory.LohP != 0, }; - private List GetGenerationData(TargetPointer generationTableArrayStart) - { - uint generationTableLength = _target.ReadGlobal(Constants.Globals.TotalGenerationCount); - uint generationSize = _target.GetTypeInfo(DataType.Generation).Size ?? throw new InvalidOperationException("Type Generation has no size"); - List generationTable = []; - for (uint i = 0; i < generationTableLength; i++) - { - TargetPointer generationAddress = generationTableArrayStart + i * generationSize; - generationTable.Add(_target.ProcessedData.GetOrAdd(generationAddress)); - } - List generationDataList = generationTable.Select(gen => - new GCGenerationData() - { - StartSegment = gen.StartSegment, - AllocationStart = gen.AllocationStart ?? 0, - AllocationContextPointer = gen.AllocationContext.Pointer, - AllocationContextLimit = gen.AllocationContext.Limit, - }).ToList(); - return generationDataList; - } - - private List GetFillPointers(Data.CFinalize cFinalize) - { - uint fillPointersLength = _target.ReadGlobal(Constants.Globals.CFinalizeFillPointersLength); - TargetPointer fillPointersArrayStart = cFinalize.FillPointers; - List fillPointers = []; - for (uint i = 0; i < fillPointersLength; i++) - fillPointers.Add(_target.ReadPointer(fillPointersArrayStart + i * (ulong)_target.PointerSize)); - return fillPointers; - } - private GCType GetGCType() { string[] identifiers = ((IGC)this).GetGCIdentifiers(); @@ -269,4 +245,9 @@ private bool IsDatasEnabled() string[] identifiers = ((IGC)this).GetGCIdentifiers(); return identifiers.Contains(GCIdentifiers.DynamicHeapCount); } + + private sealed class GCHeap_WKS + { + + } } 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..02e30a9ad53691 --- /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 64% 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 34f12b2c7b2e10..10427de610b388 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); @@ -30,6 +31,15 @@ public GCHeap_svr(Target target, TargetPointer address) SavedSweepEphemeralStart = target.ReadPointer(address + (ulong)type.Fields[nameof(SavedSweepEphemeralStart)].Offset); OOMData = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(OOMData)].Offset); + + InternalRootArray = 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; } @@ -46,4 +56,13 @@ public GCHeap_svr(Target target, TargetPointer address) 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/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 205ee2fbce8fe7..df0c8dafedd26e 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -281,6 +281,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 @@ -343,7 +349,15 @@ internal struct DacpOomData public ulong gc_index; public int fgm; public ulong size; - public int loh_p; // BOOL -> int + 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] @@ -483,9 +497,9 @@ internal unsafe partial interface ISOSDacInterface [PreserveSig] 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] @@ -611,14 +625,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 ed8f402bb38a32..d9030e0dc52fba 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1110,10 +1110,103 @@ 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.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.SVRGetHeapData(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) + { + if (data == null) + { + return HResults.E_INVALIDARG; + } + + 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 WKSGetHeapData + GCHeapData heapData = gc.WKSGetHeapData(); + + 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, void* data) => _legacyImpl is not null ? _legacyImpl.GetHeapSegmentData(seg, data) : HResults.E_NOTIMPL; @@ -2349,7 +2442,7 @@ int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, DacpOomData* data) data->gc_index = oomData.GCIndex.Value; data->fgm = oomData.Fgm; data->size = oomData.Size.Value; - data->loh_p = oomData.LohP ? 1 : 0; + data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; } catch (System.Exception ex) { @@ -2399,7 +2492,7 @@ int ISOSDacInterface.GetOOMStaticData(DacpOomData* data) data->gc_index = oomData.GCIndex.Value; data->fgm = oomData.Fgm; data->size = oomData.Size.Value; - data->loh_p = oomData.LohP ? 1 : 0; + data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE; } catch (System.Exception ex) { From 51e75452c86345b199e6b7cb2338d4fa7d1387b4 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:59:35 -0400 Subject: [PATCH 04/14] Implement GCInterestingInfoData APIs --- .../Contracts/IGC.cs | 5 + .../Constants.cs | 8 +- .../Contracts/GC/GC_1.cs | 27 +++- .../Legacy/SOSDacImpl.cs | 148 +++++++++++++++++- 4 files changed, 182 insertions(+), 6 deletions(-) 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 079c2e7198eb07..879814cb6b73d1 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 @@ -36,6 +36,11 @@ public readonly struct GCHeapData 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 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 e352b1145a0a8d..59e0866a9f69ee 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,14 @@ public static class Globals public const string Heaps = nameof(Heaps); public const string CurrentGCState = nameof(CurrentGCState); public const string DynamicAdaptationMode = nameof(DynamicAdaptationMode); - public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength); + 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); 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 8627e1c2ab1fad..279847bb5b1863 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 @@ -150,6 +150,23 @@ private GCHeapData GetGCHeapDataFromHeap(IGCHeap heap) 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(), }; } @@ -180,10 +197,18 @@ 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.WKSGetOOMData() { if (GetGCType() != GCType.Workstation) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index d9030e0dc52fba..8a18428af6a41b 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -3051,10 +3051,150 @@ 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)!; + + GCHeapData heapData = gc.SVRGetHeapData(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(/*struct DacpGCInterestingInfoData*/ 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 WKSGetHeapData + GCHeapData heapData = gc.WKSGetHeapData(); + + 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; + + // 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; #endregion ISOSDacInterface3 From a06c5bbabd915d7ed9359fff8f52d99ea18fea68 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:26:41 -0400 Subject: [PATCH 05/14] implement HeapSegmentData --- .../Contracts/IGC.cs | 1 + .../Constants.cs | 1 + .../Contracts/GC/GC_1.cs | 8 ++ .../Legacy/ISOSDacInterface.cs | 18 ++- .../Legacy/SOSDacImpl.cs | 127 +++++++++++++++++- 5 files changed, 150 insertions(+), 5 deletions(-) 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 879814cb6b73d1..ed7e3ec4edcf2d 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 @@ -90,6 +90,7 @@ public interface IGC : IContract uint GetCurrentGCState() => throw new NotImplementedException(); int GetDynamicAdaptationMode() => throw new NotImplementedException(); GCHeapSegmentData GetHeapSegmentData(TargetPointer segmentAddress) => throw new NotImplementedException(); + IReadOnlyList GetGlobalMechanisms() => throw new NotImplementedException(); IEnumerable GetGCHeaps() => throw new NotImplementedException(); /* WKS only APIs */ 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 59e0866a9f69ee..4801384c74a86e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -86,6 +86,7 @@ public static class Globals public const string Heaps = nameof(Heaps); public const string CurrentGCState = nameof(CurrentGCState); public const string DynamicAdaptationMode = nameof(DynamicAdaptationMode); + public const string GCGlobalMechanisms = nameof(GCGlobalMechanisms); public const string TotalGenerationCount = nameof(TotalGenerationCount); public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength); 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 279847bb5b1863..97ea635d1b1b28 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 @@ -99,6 +99,14 @@ GCHeapSegmentData IGC.GetHeapSegmentData(TargetPointer segmentAddress) }; } + 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) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index df0c8dafedd26e..9190226633fbc7 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -341,6 +341,22 @@ 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; @@ -491,7 +507,7 @@ 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, DacpOomData* data); [PreserveSig] diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 8a18428af6a41b..2493686c512971 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1207,8 +1207,74 @@ int ISOSDacInterface.GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data) return hr; } - int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, void* data) - => _legacyImpl is not null ? _legacyImpl.GetHeapSegmentData(seg, data) : HResults.E_NOTIMPL; + 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.SVRGetHeapData(segmentData.Heap) : gc.WKSGetHeapData(); + 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) { @@ -3092,7 +3158,7 @@ int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAdd return hr; } - int ISOSDacInterface3.GetGCInterestingInfoStaticData(/*struct DacpGCInterestingInfoData*/ DacpGCInterestingInfoData* data) + int ISOSDacInterface3.GetGCInterestingInfoStaticData(DacpGCInterestingInfoData* data) { int hr = HResults.S_OK; @@ -3138,6 +3204,10 @@ private static void PopulateGCInterestingInfoData(GCHeapData heapData, DacpGCInt { *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; @@ -3196,7 +3266,56 @@ private static void VerifyGCInterestingInfoData(DacpGCInterestingInfoData* cdacD #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 From 93b18a9088261847a9b1bfd74365fc6e300898c9 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:07:43 -0400 Subject: [PATCH 06/14] fix GetGCDynamicAdaptationMode --- .../Contracts/IGC.cs | 2 +- .../Contracts/GC/GC_1.cs | 11 +++++------ .../cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs | 10 +++++++++- 3 files changed, 15 insertions(+), 8 deletions(-) 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 ed7e3ec4edcf2d..65a5387db7be0f 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 @@ -88,7 +88,7 @@ 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(); - int GetDynamicAdaptationMode() => 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(); 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 97ea635d1b1b28..cb1ba5c387f010 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 @@ -72,14 +72,13 @@ uint IGC.GetCurrentGCState() return _target.Read(_target.ReadGlobalPointer(Constants.Globals.CurrentGCState)); } - int IGC.GetDynamicAdaptationMode() + bool IGC.TryGetGCDynamicAdaptationMode(out int mode) { - // not enabled = -1 - // dynamic_adaptation_default = 0, - // dynamic_adaptation_to_application_sizes = 1, + mode = default; if (!IsDatasEnabled()) - return -1; - return _target.Read(_target.ReadGlobalPointer(Constants.Globals.DynamicAdaptationMode)); + return false; + mode = _target.Read(_target.ReadGlobalPointer(Constants.Globals.DynamicAdaptationMode)); + return true; } GCHeapSegmentData IGC.GetHeapSegmentData(TargetPointer segmentAddress) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 2493686c512971..340bde83a3d890 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -3687,7 +3687,15 @@ int ISOSDacInterface16.GetGCDynamicAdaptationMode(int* pDynamicAdaptationMode) throw new ArgumentException(); IGC gc = _target.Contracts.GC; - *pDynamicAdaptationMode = gc.GetDynamicAdaptationMode(); + if (gc.TryGetGCDynamicAdaptationMode(out int mode)) + { + *pDynamicAdaptationMode = mode; + } + else + { + *pDynamicAdaptationMode = -1; + hr = HResults.S_FALSE; + } } catch (System.Exception ex) { From 183d35c8edd160b98686b142ce5d39d8f6ec63c5 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:11:23 -0400 Subject: [PATCH 07/14] fix assert --- .../managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 340bde83a3d890..6c740c79168221 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -3708,9 +3708,9 @@ int ISOSDacInterface16.GetGCDynamicAdaptationMode(int* pDynamicAdaptationMode) int dynamicAdaptationModeLocal; int hrLocal = _legacyImpl16.GetGCDynamicAdaptationMode(&dynamicAdaptationModeLocal); Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); - if (hr == HResults.S_OK && pDynamicAdaptationMode != null) + if (hr == HResults.S_OK || hr == HResults.S_FALSE) { - Debug.Assert(*pDynamicAdaptationMode == dynamicAdaptationModeLocal); + Debug.Assert(pDynamicAdaptationMode == null || *pDynamicAdaptationMode == dynamicAdaptationModeLocal); } } #endif From a1a032be386342f212e3d418ffe4042632b308ab Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:55:13 -0400 Subject: [PATCH 08/14] fix --- .../Contracts/GC/GCHeapWKS.cs | 2 +- .../Data/GC/GCHeapSVR.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 a41ac1e4f55535..45a40f897aa18a 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 @@ -24,7 +24,7 @@ public GCHeapWKS(Target target) OOMData = target.ProcessedData.GetOrAdd(target.ReadGlobalPointer(Constants.Globals.GCHeapOOMData)); - InternalRootArray = target.ReadGlobalPointer(Constants.Globals.GCHeapInternalRootArray); + 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; 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 10427de610b388..653e20ce03cc5f 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 @@ -32,7 +32,7 @@ public GCHeapSVR(Target target, TargetPointer address) OOMData = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(OOMData)].Offset); - InternalRootArray = address + (ulong)type.Fields[nameof(InternalRootArray)].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; From 9524a93393ec3e9abb77b6198c5870c0556f97fb Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:39:55 -0400 Subject: [PATCH 09/14] update docs --- docs/design/datacontracts/GC.md | 157 +++++++++++++----- .../gc/datadescriptor/datadescriptor.inc | 1 - 2 files changed, 111 insertions(+), 47 deletions(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index d266498b0ec452..b5084acaf123b1 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 @@ -80,6 +89,14 @@ 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 | @@ -97,6 +114,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) | @@ -108,6 +129,14 @@ 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 | @@ -225,6 +254,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); @@ -243,41 +280,26 @@ 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; } @@ -302,6 +324,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 */); @@ -320,12 +351,39 @@ 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; +} +``` + +Helper methods: +```csharp +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; @@ -343,20 +401,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.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index 62fd739189079f..865c3f36257932 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -114,7 +114,6 @@ CDAC_GLOBAL_POINTER(GCHeapInterestingData, cdac_data::Int 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 From 22e03de12ca3800a3c67c2af125948d1f9f54dd3 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:48:15 -0400 Subject: [PATCH 10/14] finish docs --- docs/design/datacontracts/GC.md | 136 ++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 7 deletions(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index b5084acaf123b1..7b5919f4da14d4 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -40,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 @@ -61,16 +87,22 @@ public readonly struct GCGenerationData void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr); // Gets the current GC state enum value uint GetCurrentGCState(); - // Gets the current GC heap dynamic adaptation mode. -1 if not enabled. - int GetDynamicAdaptationMode(); - // 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(); + GCOOMData WKSGetOOMData(); /* SVR only APIs */ GCHeapData SVRGetHeapData(TargetPointer heapAddress); + GCOOMData SVRGetOOMData(TargetPointer heapAddress); ``` ## Version 1 @@ -101,6 +133,24 @@ Data descriptors used: | `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 | @@ -207,15 +257,50 @@ uint IGC.GetCurrentGCState() return 0; } -int IGC.GetDynamicAdaptationMode() +bool IGC.TryGetDynamicAdaptationMode(out int mode) { + mode = default; string[] gcIdentifiers = GetGCIdentifiers(); - if (gcIdentifiers.Contains("dynamic_heap)) + if (!gcIdentifiers.Contains("dynamic_heap)) { - return target.read(target.ReadGlobalPointer("DynamicAdaptationMode")); + return false; } - return -1; + 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() @@ -303,6 +388,16 @@ GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) return data; } + +GCOOMData IGC.WKSGetOOMData() +{ + string[] gcIdentifiers = GetGCIdentifiers(); + if (!gcType.Contains("workstation")) + throw new InvalidOperationException(); + + TargetPointer oomHistory = target.ReadGlobalPointer("GCHeapOOMData"); + return GetGCOOMData(oomHistoryData); +} ``` server GC only APIs @@ -374,6 +469,16 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) return data; } + +GCOOMData IGC.SVRGetOOMData(TargetPointer heapAddress) +{ + string[] gcIdentifiers = GetGCIdentifiers(); + if (!gcType.Contains("server")) + throw new InvalidOperationException(); + + TargetPointer oomHistory = target.ReadPointer(heapAddress + /* GCHeap::OOMData offset */); + return GetGCOOMData(oomHistory); +} ``` Helper methods: @@ -424,4 +529,21 @@ private List ReadGCHeapDataArray(TargetPointer arrayStart, uint len arr.Add(target.ReadNUInt(arrayStart + (i * target.PointerSize))); return arr; } + +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; +} ``` From e645e66df3c63f9eeb4fa51cd358852ad1f0c6f5 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:57:57 -0400 Subject: [PATCH 11/14] rename OOM -> Oom --- docs/design/datacontracts/GC.md | 64 +++++++++---------- .../gc/datadescriptor/datadescriptor.h | 2 +- .../gc/datadescriptor/datadescriptor.inc | 28 ++++---- .../Contracts/IGC.cs | 6 +- .../DataType.cs | 2 +- .../Constants.cs | 2 +- .../Contracts/GC/GCHeapWKS.cs | 4 +- .../Contracts/GC/GC_1.cs | 16 ++--- .../Contracts/GC/IGCHeap.cs | 2 +- .../Data/GC/GCHeapSVR.cs | 4 +- .../Data/GC/OOMHistory.cs | 8 +-- .../Legacy/SOSDacImpl.cs | 4 +- 12 files changed, 71 insertions(+), 71 deletions(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 7b5919f4da14d4..1046176645b6e6 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -54,7 +54,7 @@ public readonly struct GCHeapSegmentData public TargetPointer Heap { get; init; } } -public readonly struct GCOOMData +public readonly struct GCOomData { public int Reason { get; init; } public TargetNUInt AllocSize { get; init; } @@ -98,11 +98,11 @@ public readonly struct GCOOMData /* WKS only APIs */ GCHeapData WKSGetHeapData(); - GCOOMData WKSGetOOMData(); + GCOomData WKSGetOomData(); /* SVR only APIs */ GCHeapData SVRGetHeapData(TargetPointer heapAddress); - GCOOMData SVRGetOOMData(TargetPointer heapAddress); + GCOomData SVRGetOomData(TargetPointer heapAddress); ``` ## Version 1 @@ -121,7 +121,7 @@ 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` | 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) | @@ -142,15 +142,15 @@ Data descriptors used: | `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 | +| `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 | @@ -179,7 +179,7 @@ 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) | +| `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) | @@ -389,14 +389,14 @@ GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) return data; } -GCOOMData IGC.WKSGetOOMData() +GCOomData IGC.WKSGetOomData() { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("workstation")) throw new InvalidOperationException(); - TargetPointer oomHistory = target.ReadGlobalPointer("GCHeapOOMData"); - return GetGCOOMData(oomHistoryData); + TargetPointer oomHistory = target.ReadGlobalPointer("GCHeapOomData"); + return GetGCOomData(oomHistoryData); } ``` @@ -470,14 +470,14 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) return data; } -GCOOMData IGC.SVRGetOOMData(TargetPointer heapAddress) +GCOomData IGC.SVRGetOomData(TargetPointer heapAddress) { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("server")) throw new InvalidOperationException(); - TargetPointer oomHistory = target.ReadPointer(heapAddress + /* GCHeap::OOMData offset */); - return GetGCOOMData(oomHistory); + TargetPointer oomHistory = target.ReadPointer(heapAddress + /* GCHeap::OomData offset */); + return GetGCOomData(oomHistory); } ``` @@ -530,19 +530,19 @@ private List ReadGCHeapDataArray(TargetPointer arrayStart, uint len return arr; } -private GCOOMData GetGCOOMData(TargetPointer 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 */); + 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; } diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.h b/src/coreclr/gc/datadescriptor/datadescriptor.h index 42efeac7044408..d77cf6e3e2a8ad 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.h +++ b/src/coreclr/gc/datadescriptor/datadescriptor.h @@ -48,7 +48,7 @@ struct cdac_data static constexpr GC_NAMESPACE::gc_heap*** Heaps = &GC_NAMESPACE::gc_heap::g_heaps; #endif // SERVER_GC - GC_HEAP_FIELD(OOMData, oom_info) + GC_HEAP_FIELD(OomData, oom_info) /* For use in GCHeapDetails APIs */ GC_HEAP_FIELD(MarkArray, mark_array) diff --git a/src/coreclr/gc/datadescriptor/datadescriptor.inc b/src/coreclr/gc/datadescriptor/datadescriptor.inc index 865c3f36257932..1091b9a9f9f0e9 100644 --- a/src/coreclr/gc/datadescriptor/datadescriptor.inc +++ b/src/coreclr/gc/datadescriptor/datadescriptor.inc @@ -26,7 +26,7 @@ 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, /*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) @@ -66,18 +66,18 @@ CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Heap, offsetof(GC_NAMESPACE::heap_segm #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_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() @@ -106,7 +106,7 @@ 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(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) 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 65a5387db7be0f..9eb050e4e1e61e 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 @@ -64,7 +64,7 @@ public readonly struct GCHeapSegmentData public TargetPointer Heap { get; init; } } -public readonly struct GCOOMData +public readonly struct GCOomData { public int Reason { get; init; } public TargetNUInt AllocSize { get; init; } @@ -95,11 +95,11 @@ public interface IGC : IContract /* WKS only APIs */ GCHeapData WKSGetHeapData() => throw new NotImplementedException(); - GCOOMData WKSGetOOMData() => throw new NotImplementedException(); + GCOomData WKSGetOomData() => throw new NotImplementedException(); /* SVR only APIs */ GCHeapData SVRGetHeapData(TargetPointer heapAddress) => throw new NotImplementedException(); - GCOOMData SVRGetOOMData(TargetPointer heapAddress) => throw new NotImplementedException(); + GCOomData SVRGetOomData(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 0911d974d0ac0d..cb10e548ff245e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -139,5 +139,5 @@ public enum DataType Generation, CFinalize, HeapSegment, - OOMHistory, + 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 4801384c74a86e..3c6ecf73791bf5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -107,7 +107,7 @@ 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 GCHeapOomData = nameof(GCHeapOomData); public const string GCHeapInternalRootArray = nameof(GCHeapInternalRootArray); public const string GCHeapInternalRootArrayIndex = nameof(GCHeapInternalRootArrayIndex); public const string GCHeapHeapAnalyzeSuccess = nameof(GCHeapHeapAnalyzeSuccess); 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 45a40f897aa18a..412c6c71a620cb 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 @@ -22,7 +22,7 @@ public GCHeapWKS(Target target) if (target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralStart, out TargetPointer? savedSweepEphemeralStartPtr)) SavedSweepEphemeralStart = target.ReadPointer(savedSweepEphemeralStartPtr.Value); - OOMData = target.ProcessedData.GetOrAdd(target.ReadGlobalPointer(Constants.Globals.GCHeapOOMData)); + 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)); @@ -47,7 +47,7 @@ public GCHeapWKS(Target target) public TargetPointer? SavedSweepEphemeralSeg { get; } public TargetPointer? SavedSweepEphemeralStart { get; } - public Data.OOMHistory OOMData { get; } + public Data.OomHistory OomData { get; } public TargetPointer InternalRootArray { get; } public TargetNUInt InternalRootArrayIndex { 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 cb1ba5c387f010..26b11e8fbbe020 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 @@ -216,27 +216,27 @@ private List ReadGCHeapDataArray(TargetPointer arrayStart, uint len return arr; } - GCOOMData IGC.WKSGetOOMData() + GCOomData IGC.WKSGetOomData() { if (GetGCType() != GCType.Workstation) throw new InvalidOperationException("WKSGetHeapData is only valid for Workstation GC."); - TargetPointer oomHistory = _target.ReadGlobalPointer(Constants.Globals.GCHeapOOMData); - Data.OOMHistory oomHistoryData = _target.ProcessedData.GetOrAdd(oomHistory); - return GetGCOOMData(oomHistoryData); + TargetPointer oomHistory = _target.ReadGlobalPointer(Constants.Globals.GCHeapOomData); + Data.OomHistory oomHistoryData = _target.ProcessedData.GetOrAdd(oomHistory); + return GetGCOomData(oomHistoryData); } - GCOOMData IGC.SVRGetOOMData(TargetPointer heapAddress) + GCOomData IGC.SVRGetOomData(TargetPointer heapAddress) { if (GetGCType() != GCType.Server) throw new InvalidOperationException("GetHeapData is only valid for Server GC."); Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); - return GetGCOOMData(heap.OOMData); + return GetGCOomData(heap.OomData); } - private static GCOOMData GetGCOOMData(Data.OOMHistory oomHistory) - => new GCOOMData() + private static GCOomData GetGCOomData(Data.OomHistory oomHistory) + => new GCOomData() { Reason = oomHistory.Reason, AllocSize = oomHistory.AllocSize, 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 02e30a9ad53691..cab7d0ee4d9f1c 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 @@ -18,7 +18,7 @@ internal interface IGCHeap TargetPointer? SavedSweepEphemeralSeg { get; } TargetPointer? SavedSweepEphemeralStart { get; } - Data.OOMHistory OOMData { get; } + Data.OomHistory OomData { get; } TargetPointer InternalRootArray { get; } TargetNUInt InternalRootArrayIndex { 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 653e20ce03cc5f..c3f7520bb4da8c 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 @@ -30,7 +30,7 @@ public GCHeapSVR(Target target, TargetPointer address) 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); + 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); @@ -55,7 +55,7 @@ public GCHeapSVR(Target target, TargetPointer address) public TargetPointer? SavedSweepEphemeralSeg { get; } public TargetPointer? SavedSweepEphemeralStart { get; } - public OOMHistory OOMData { get; } + public OomHistory OomData { get; } public TargetPointer InternalRootArray { get; } public TargetNUInt InternalRootArrayIndex { 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 index 29b9d2b8314153..42f21fc5a589c8 100644 --- 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 @@ -3,12 +3,12 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; -internal sealed class OOMHistory : IData +internal sealed class OomHistory : IData { - static OOMHistory IData.Create(Target target, TargetPointer address) => new OOMHistory(target, address); - public OOMHistory(Target target, TargetPointer address) + 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); + 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); diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index e7942daf4ac70d..9243bd39297910 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -2567,7 +2567,7 @@ int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, DacpOomData* data) if (!gcIdentifiers.Contains(GCIdentifiers.Server)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCOOMData oomData = gc.SVRGetOOMData(oomAddr.ToTargetPointer(_target)); + GCOomData oomData = gc.SVRGetOomData(oomAddr.ToTargetPointer(_target)); data->reason = oomData.Reason; data->alloc_size = oomData.AllocSize.Value; @@ -2617,7 +2617,7 @@ int ISOSDacInterface.GetOOMStaticData(DacpOomData* data) // This method is only valid for workstation GC mode if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCOOMData oomData = gc.WKSGetOOMData(); + GCOomData oomData = gc.WKSGetOomData(); data->reason = oomData.Reason; data->alloc_size = oomData.AllocSize.Value; From 98889400ae3bb149acad2e04a28780ac0f1c5994 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:07:14 -0400 Subject: [PATCH 12/14] rename APIs using overloading --- docs/design/datacontracts/GC.md | 105 +++++++++--------- .../Contracts/IGC.cs | 16 +-- .../Contracts/GC/GC_1.cs | 16 +-- .../Legacy/SOSDacImpl.cs | 23 ++-- 4 files changed, 82 insertions(+), 78 deletions(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 1046176645b6e6..7bdb7347844d7a 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -96,13 +96,17 @@ public readonly struct GCOomData // Returns pointers to all GC heaps IEnumerable GetGCHeaps(); - /* WKS only APIs */ - GCHeapData WKSGetHeapData(); - GCOomData WKSGetOomData(); + // 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. - /* SVR only APIs */ - GCHeapData SVRGetHeapData(TargetPointer heapAddress); - GCOomData SVRGetOomData(TargetPointer heapAddress); + // Gets data about a GC heap + GCHeapData GetHeapData(); + GCHeapData GetHeapData(TargetPointer heapAddress); + + // Gets data about a managed OOM occurance + GCOomData GetOomData(); + GCOomData GetOomData(TargetPointer heapAddress); ``` ## Version 1 @@ -320,9 +324,49 @@ IEnumerable IGC.GetGCHeaps() } ``` -workstation GC only APIs +GetOomData ```csharp -GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) +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.GetHeapData() { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("workstation")) @@ -389,20 +433,7 @@ GCHeapData IGC.WKSGetHeapData(TargetPointer heapAddress) return data; } -GCOomData IGC.WKSGetOomData() -{ - string[] gcIdentifiers = GetGCIdentifiers(); - if (!gcType.Contains("workstation")) - throw new InvalidOperationException(); - - TargetPointer oomHistory = target.ReadGlobalPointer("GCHeapOomData"); - return GetGCOomData(oomHistoryData); -} -``` - -server GC only APIs -```csharp -GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) +GCHeapData IGC.GetHeapData(TargetPointer heapAddress) { string[] gcIdentifiers = GetGCIdentifiers(); if (!gcType.Contains("server")) @@ -470,19 +501,6 @@ GCHeapData IGC.SVRGetHeapData(TargetPointer heapAddress) return data; } -GCOomData IGC.SVRGetOomData(TargetPointer heapAddress) -{ - string[] gcIdentifiers = GetGCIdentifiers(); - if (!gcType.Contains("server")) - throw new InvalidOperationException(); - - TargetPointer oomHistory = target.ReadPointer(heapAddress + /* GCHeap::OomData offset */); - return GetGCOomData(oomHistory); -} -``` - -Helper methods: -```csharp private List GetGenerationData(TargetPointer generationTableArrayStart) { uint generationTableLength = target.ReadGlobal("TotalGenerationCount"); @@ -529,21 +547,4 @@ private List ReadGCHeapDataArray(TargetPointer arrayStart, uint len arr.Add(target.ReadNUInt(arrayStart + (i * target.PointerSize))); return arr; } - -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; -} ``` 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 9eb050e4e1e61e..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 @@ -93,13 +93,15 @@ public interface IGC : IContract IReadOnlyList GetGlobalMechanisms() => throw new NotImplementedException(); IEnumerable GetGCHeaps() => throw new NotImplementedException(); - /* WKS only APIs */ - GCHeapData WKSGetHeapData() => throw new NotImplementedException(); - GCOomData WKSGetOomData() => throw new NotImplementedException(); - - /* SVR only APIs */ - GCHeapData SVRGetHeapData(TargetPointer heapAddress) => throw new NotImplementedException(); - GCOomData SVRGetOomData(TargetPointer heapAddress) => throw new NotImplementedException(); + // workstation variant + GCHeapData GetHeapData() => throw new NotImplementedException(); + // server variant + GCHeapData GetHeapData(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.Contracts/Contracts/GC/GC_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GC/GC_1.cs index 26b11e8fbbe020..52eab7b6457df2 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 @@ -119,18 +119,18 @@ IEnumerable IGC.GetGCHeaps() } } - GCHeapData IGC.WKSGetHeapData() + GCHeapData IGC.GetHeapData() { if (GetGCType() != GCType.Workstation) - throw new InvalidOperationException("WKSGetHeapData is only valid for Workstation GC."); + throw new InvalidOperationException("GetHeapData() is only valid for Workstation GC."); 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); @@ -216,20 +216,20 @@ private List ReadGCHeapDataArray(TargetPointer arrayStart, uint len return arr; } - GCOomData IGC.WKSGetOomData() + GCOomData IGC.GetOomData() { if (GetGCType() != GCType.Workstation) - throw new InvalidOperationException("WKSGetHeapData is only valid for Workstation GC."); + 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.SVRGetOomData(TargetPointer heapAddress) + GCOomData IGC.GetOomData(TargetPointer heapAddress) { if (GetGCType() != GCType.Server) - throw new InvalidOperationException("GetHeapData is only valid for Server GC."); + throw new InvalidOperationException("GetOomData(TargetPointer heap) is only valid for Server GC."); Data.GCHeapSVR heap = _target.ProcessedData.GetOrAdd(heapAddress); return GetGCOomData(heap.OomData); diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 9243bd39297910..258c7f352a3c2a 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -852,7 +852,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; @@ -985,7 +985,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; @@ -1125,7 +1125,7 @@ int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, DacpGcHeapAnalyzeDa if (!gcIdentifiers.Contains(GCIdentifiers.Server)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCHeapData heapData = gc.SVRGetHeapData(addr.ToTargetPointer(_target)); + GCHeapData heapData = gc.GetHeapData(addr.ToTargetPointer(_target)); data->heapAddr = addr; data->internal_root_array = heapData.InternalRootArray.ToClrDataAddress(_target); @@ -1176,8 +1176,8 @@ int ISOSDacInterface.GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data) if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - // For workstation GC, use WKSGetHeapData - GCHeapData heapData = gc.WKSGetHeapData(); + // 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); @@ -1235,7 +1235,7 @@ int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, DacpHeapSegmentData* // 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.SVRGetHeapData(segmentData.Heap) : gc.WKSGetHeapData(); + GCHeapData heapData = gcIdentifiers.Contains(GCIdentifiers.Server) ? gc.GetHeapData(segmentData.Heap) : gc.GetHeapData(); if (seg.ToTargetPointer(_target) == heapData.EphemeralHeapSegment) { data->highAllocMark = heapData.AllocAllocated.ToClrDataAddress(_target); @@ -2567,7 +2567,7 @@ int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, DacpOomData* data) if (!gcIdentifiers.Contains(GCIdentifiers.Server)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCOomData oomData = gc.SVRGetOomData(oomAddr.ToTargetPointer(_target)); + GCOomData oomData = gc.GetOomData(oomAddr.ToTargetPointer(_target)); data->reason = oomData.Reason; data->alloc_size = oomData.AllocSize.Value; @@ -2617,7 +2617,7 @@ int ISOSDacInterface.GetOOMStaticData(DacpOomData* data) // This method is only valid for workstation GC mode if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCOomData oomData = gc.WKSGetOomData(); + GCOomData oomData = gc.GetOomData(); data->reason = oomData.Reason; data->alloc_size = oomData.AllocSize.Value; @@ -3200,7 +3200,8 @@ int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAdd if (!gcIdentifiers.Contains(GCIdentifiers.Server)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - GCHeapData heapData = gc.SVRGetHeapData(interestingInfoAddr.ToTargetPointer(_target)); + // For server GC, use GetHeapData(TargetPointer heap) + GCHeapData heapData = gc.GetHeapData(interestingInfoAddr.ToTargetPointer(_target)); PopulateGCInterestingInfoData(heapData, data); } @@ -3241,8 +3242,8 @@ int ISOSDacInterface3.GetGCInterestingInfoStaticData(DacpGCInterestingInfoData* if (!gcIdentifiers.Contains(GCIdentifiers.Workstation)) throw Marshal.GetExceptionForHR(HResults.E_FAIL)!; - // For workstation GC, use WKSGetHeapData - GCHeapData heapData = gc.WKSGetHeapData(); + // For workstation GC, use GetHeapData() + GCHeapData heapData = gc.GetHeapData(); PopulateGCInterestingInfoData(heapData, data); } From 4f799483fdb830ee4727e09cc094c21d424e8cd2 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:06:11 -0400 Subject: [PATCH 13/14] copilot comments --- .../Contracts/GC/GC_1.cs | 5 ----- .../cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs | 6 ------ 2 files changed, 11 deletions(-) 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 52eab7b6457df2..cf101d271b3767 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 @@ -277,9 +277,4 @@ private bool IsDatasEnabled() string[] identifiers = ((IGC)this).GetGCIdentifiers(); return identifiers.Contains(GCIdentifiers.DynamicHeapCount); } - - private sealed class GCHeap_WKS - { - - } } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 258c7f352a3c2a..ab17dc031c55bf 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -1157,13 +1157,7 @@ int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, DacpGcHeapAnalyzeDa } int ISOSDacInterface.GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data) { - if (data == null) - { - return HResults.E_INVALIDARG; - } - int hr = HResults.S_OK; - try { if (data == null) From f361a80ffd9bc4999dcfca74152adb4f458f1c04 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:22:26 -0400 Subject: [PATCH 14/14] Update docs/design/datacontracts/GC.md Co-authored-by: Noah Falk --- docs/design/datacontracts/GC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/datacontracts/GC.md b/docs/design/datacontracts/GC.md index 7bdb7347844d7a..bbb6d3ebafb121 100644 --- a/docs/design/datacontracts/GC.md +++ b/docs/design/datacontracts/GC.md @@ -265,7 +265,7 @@ bool IGC.TryGetDynamicAdaptationMode(out int mode) { mode = default; string[] gcIdentifiers = GetGCIdentifiers(); - if (!gcIdentifiers.Contains("dynamic_heap)) + if (!gcIdentifiers.Contains("dynamic_heap")) { return false; }