From 41767c98e521e15e2a91c924483da7f57dc06c48 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:57:46 -0500 Subject: [PATCH 01/19] initial support for HAVE_GCCOVER --- src/coreclr/debug/runtimeinfo/contracts.jsonc | 1 + .../debug/runtimeinfo/datadescriptor.h | 12 +++++ src/coreclr/vm/codeversion.h | 3 ++ src/coreclr/vm/gccover.h | 6 +++ src/coreclr/vm/method.hpp | 3 ++ .../ContractRegistry.cs | 6 ++- .../Contracts/IGCCover.cs | 16 ++++++ .../Contracts/IRuntimeTypeSystem.cs | 2 + .../DataType.cs | 1 + .../Contracts/CodeVersions_1.cs | 2 + .../Contracts/GCCoverFactory.cs | 18 +++++++ .../Contracts/GCCover_1.cs | 49 +++++++++++++++++++ .../Contracts/RuntimeTypeSystem_1.cs | 9 ++++ .../Data/MethodDesc.cs | 8 ++- .../Data/NativeCodeVersionNode.cs | 6 +++ .../CachingContractRegistry.cs | 1 + .../cdacreader/src/Legacy/SOSDacImpl.cs | 16 ++++-- 17 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc index ae8cb684ab2c72..307586be5cd201 100644 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -14,6 +14,7 @@ "EcmaMetadata" : 1, "Exception": 1, "ExecutionManager": 2, + "GCCover": 1, "Loader": 1, "Object": 1, "PlatformMetadata": 1, diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index b3894f4efa71d1..e9874d9139971d 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -331,6 +331,9 @@ CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::Flags3AndTokenRemainder) CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, EntryPointFlags, cdac_data::EntryPointFlags) CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data::CodeData) +#ifdef HAVE_GCCOVER +CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, cdac_data::GCCoverageInfo) +#endif // HAVE_GCCOVER CDAC_TYPE_END(MethodDesc) CDAC_TYPE_BEGIN(MethodDescChunk) @@ -522,6 +525,9 @@ CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, MethodDesc, cdac_data::NativeCode) CDAC_TYPE_FIELD(NativeCodeVersionNode, /*uint32*/, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(NativeCodeVersionNode, /*nuint*/, ILVersionId, cdac_data::ILVersionId) +#ifdef HAVE_GCCOVER +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, GCCoverageInfo, cdac_data::GCCoverageInfo) +#endif // HAVE_GCCOVER CDAC_TYPE_END(NativeCodeVersionNode) CDAC_TYPE_BEGIN(ILCodeVersionNode) @@ -535,6 +541,12 @@ CDAC_TYPE_BEGIN(ProfControlBlock) CDAC_TYPE_FIELD(ProfControlBlock, /*uint64*/, GlobalEventMask, offsetof(ProfControlBlock, globalEventMask)) CDAC_TYPE_END(ProfControlBlock) +#ifdef HAVE_GCCOVER +CDAC_TYPE_BEGIN(GCCoverageInfo) +CDAC_TYPE_FIELD(GCCoverageInfo, /*pointer*/, SavedCode, offsetof(GCCoverageInfo, SavedCode)) +CDAC_TYPE_END(GCCoverageInfo) +#endif // HAVE_GCCOVER + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 442a2846543757..b9dc76b90e3066 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -330,6 +330,9 @@ struct cdac_data static constexpr size_t NativeCode = offsetof(NativeCodeVersionNode, m_pNativeCode); static constexpr size_t Flags = offsetof(NativeCodeVersionNode, m_flags); static constexpr size_t ILVersionId = offsetof(NativeCodeVersionNode, m_parentId); +#ifdef HAVE_GCCOVER + static constexpr size_t GCCoverageInfo = offsetof(NativeCodeVersionNode, m_gcCover); +#endif // HAVE_GCCOVER }; class NativeCodeVersionCollection diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index 182a24bb975752..aca9f183657099 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -46,6 +46,12 @@ class GCCoverageInfo { void SprinkleBreakpoints(BYTE * saveAddr, PCODE codeStart, size_t codeSize, size_t regionOffsetAdj, BOOL fZapped); }; +template<> +struct cdac_data +{ + static constexpr size_t SavedCode = offsetof(GCCoverageInfo, savedCode); +}; + typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::savedCode #ifdef _MSC_VER diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 44d03a2ffd3c16..a5ea463409028e 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1908,6 +1908,9 @@ template<> struct cdac_data static constexpr size_t Flags3AndTokenRemainder = offsetof(MethodDesc, m_wFlags3AndTokenRemainder); static constexpr size_t EntryPointFlags = offsetof(MethodDesc, m_bFlags4); static constexpr size_t CodeData = offsetof(MethodDesc, m_codeData); +#ifdef HAVE_GCCOVER + static constexpr size_t GCCoverageInfo = offsetof(MethodDesc, m_GcCover); +#endif // HAVE_GCCOVER }; #ifndef DACCESS_COMPILE diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index a81bdfe92fce95..fc03f1f13ab40a 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -58,5 +58,9 @@ internal abstract class ContractRegistry /// /// Gets an instance of the ReJIT contract for the target. /// - public abstract IReJIT ReJIT { get; } + public abstract IReJIT ReJIT { get; } + /// + /// Gets an instance of the GCCover contract for the target. + /// + public abstract IGCCover GCCover { get; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs new file mode 100644 index 00000000000000..0f8c2f998b40ff --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal interface IGCCover : IContract +{ + public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); +} + +internal readonly struct GCCover : IGCCover +{ + // throws NotImplementedException for all methods +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 31131c4475422d..baa77fe29dd473 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -166,6 +166,8 @@ internal interface IRuntimeTypeSystem : IContract public virtual bool HasNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException(); public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + public virtual TargetPointer? GetGCCoverageInfo(MethodDescHandle methodDesc) => throw new NotImplementedException(); #endregion MethodDesc inspection APIs } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index ddcdfbac5789a9..fcad68305176d5 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -76,4 +76,5 @@ public enum DataType NonVtableSlot, MethodImpl, NativeCodeSlot, + GCCoverageInfo, } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs index e6b1755004c710..f6508aab8ecb76 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs @@ -183,6 +183,8 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion }); } + + [Flags] internal enum MethodDescVersioningStateFlags : byte { diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs new file mode 100644 index 00000000000000..09b139cbe292bd --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal sealed class GCCoverFactory : IContractFactory +{ + IGCCover IContractFactory.CreateContract(Target target, int version) + { + return version switch + { + 1 => new GCCover_1(target), + _ => default(GCCover), + }; + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs new file mode 100644 index 00000000000000..82eee70e62f8f7 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Diagnostics.DataContractReader.Data; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal readonly struct GCCover_1 : IGCCover +{ + private readonly Target _target; + + internal GCCover_1(Target target) + { + _target = target; + } + + TargetPointer? IGCCover.GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) + { + if (!codeVersionHandle.Valid) + { + return null; + } + + if (!codeVersionHandle.IsExplicit) + { + // NativeCodeVersion::GetGCCoverageInfo + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); + return rts.GetGCCoverageInfo(md); + } + else + { + // NativeCodeVersionNode::GetGCCoverageInfo + NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle); + return codeVersionNode.GCCoverageInfo; + } + } + + private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle) + { + if (handle.CodeVersionNodeAddress == TargetPointer.Null) + { + throw new InvalidOperationException("Synthetic NativeCodeVersion does not have a backing node."); + } + + return _target.ProcessedData.GetOrAdd(handle.CodeVersionNodeAddress); + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index c29930d49fced7..8a2c4738954b97 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -141,6 +141,9 @@ private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.Metho public bool IsUnboxingStub => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsUnboxingStub); public TargetPointer CodeData => _desc.CodeData; + + public TargetPointer? GCCoverageInfo => _desc.GCCoverageInfo; + public bool IsIL => Classification == MethodClassification.IL || Classification == MethodClassification.Instantiated; internal bool HasNonVtableSlot => MethodDescOptionalSlots.HasNonVtableSlot(_desc.Flags); @@ -1012,6 +1015,12 @@ private TargetCodePointer GetMethodEntryPointIfExists(MethodDesc md) return _target.ReadCodePointer(addrOfSlot); } + TargetPointer? IRuntimeTypeSystem.GetGCCoverageInfo(MethodDescHandle methodDesc) + { + MethodDesc md = _methodDescs[methodDesc.Address]; + return md.GCCoverageInfo; + } + private class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries { private readonly RuntimeTypeSystem_1 _rts; diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs index eea2a616194218..472b416835b0fc 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs @@ -18,6 +18,10 @@ public MethodDesc(Target target, TargetPointer address) Flags3AndTokenRemainder = target.Read(address + (ulong)type.Fields[nameof(Flags3AndTokenRemainder)].Offset); EntryPointFlags = target.Read(address + (ulong)type.Fields[nameof(EntryPointFlags)].Offset); CodeData = target.ReadPointer(address + (ulong)type.Fields[nameof(CodeData)].Offset); + if (type.Fields.ContainsKey(nameof(GCCoverageInfo))) + { + GCCoverageInfo = target.Read(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); + } } public byte ChunkIndex { get; init; } @@ -26,7 +30,9 @@ public MethodDesc(Target target, TargetPointer address) public ushort Flags3AndTokenRemainder { get; init; } public byte EntryPointFlags { get; init; } - public TargetPointer CodeData { get; set; } + public TargetPointer CodeData { get; init; } + + public TargetPointer? GCCoverageInfo { get; init; } } internal sealed class InstantiatedMethodDesc : IData diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs index 02a7d37eb1e068..0cc7e6738c498f 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs @@ -17,6 +17,10 @@ public NativeCodeVersionNode(Target target, TargetPointer address) NativeCode = target.ReadCodePointer(address + (ulong)type.Fields[nameof(NativeCode)].Offset); Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); ILVersionId = target.ReadNUInt(address + (ulong)type.Fields[nameof(ILVersionId)].Offset); + if (type.Fields.ContainsKey(nameof(GCCoverageInfo))) + { + GCCoverageInfo = target.Read(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); + } } public TargetPointer Next { get; init; } @@ -25,4 +29,6 @@ public NativeCodeVersionNode(Target target, TargetPointer address) public TargetCodePointer NativeCode { get; init; } public uint Flags{ get; init; } public TargetNUInt ILVersionId { get; init; } + + public TargetPointer? GCCoverageInfo { get; init; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index f2602ce5e1e6ca..60727b072d2ae3 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -53,6 +53,7 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG public override IPlatformMetadata PlatformMetadata => GetContract(); public override IPrecodeStubs PrecodeStubs => GetContract(); public override IReJIT ReJIT => GetContract(); + public override IGCCover GCCover => GetContract(); private TContract GetContract() where TContract : IContract { diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index e9c9644121eb14..932c86e00aad36 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -349,16 +349,22 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes } } -#if false // TODO[cdac]: HAVE_GCCOVER + // HAVE_GCCOVER if (requestedNativeCodeVersion.Valid) { - TargetPointer gcCoverAddr = nativeCodeContract.GetGCCoverageInfo(requestedNativeCodeVersion); - if (gcCoverAddr != TargetPointer.Null) + TargetPointer? gcCoverAddr = _target.Contracts.GCCover.GetGCCoverageInfo(requestedNativeCodeVersion); + if (gcCoverAddr is not null) { - throw new NotImplementedException(); // TODO[cdac]: gc stress code copy + // HAVE_GCCOVER is enabled + if(gcCoverAddr.Value != TargetPointer.Null) + { + // In certain minidumps, we won't save the gccover information. + // (it would be unwise to do so, it is heavy and not a customer scenario). + Target.TypeInfo gcCoverageInfo = _target.GetTypeInfo(DataType.GCCoverageInfo); + data->GCStressCodeCopy = gcCoverAddr.Value.Value + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset; + } } } -#endif if (data->bIsDynamic != 0) { From 6e6461df77304e7e9ebfb15bb8068f04990ff2a1 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 15:13:32 -0500 Subject: [PATCH 02/19] fix typos --- .../Data/NativeCodeVersionNode.cs | 4 ++-- src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs index 0cc7e6738c498f..e68345e4499d23 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs @@ -19,7 +19,7 @@ public NativeCodeVersionNode(Target target, TargetPointer address) ILVersionId = target.ReadNUInt(address + (ulong)type.Fields[nameof(ILVersionId)].Offset); if (type.Fields.ContainsKey(nameof(GCCoverageInfo))) { - GCCoverageInfo = target.Read(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); + GCCoverageInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); } } @@ -27,7 +27,7 @@ public NativeCodeVersionNode(Target target, TargetPointer address) public TargetPointer MethodDesc { get; init; } public TargetCodePointer NativeCode { get; init; } - public uint Flags{ get; init; } + public uint Flags { get; init; } public TargetNUInt ILVersionId { get; init; } public TargetPointer? GCCoverageInfo { get; init; } diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 932c86e00aad36..6b54b325562498 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -356,7 +356,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes if (gcCoverAddr is not null) { // HAVE_GCCOVER is enabled - if(gcCoverAddr.Value != TargetPointer.Null) + if (gcCoverAddr.Value != TargetPointer.Null) { // In certain minidumps, we won't save the gccover information. // (it would be unwise to do so, it is heavy and not a customer scenario). From 0129e856604a946817c464838e61c8409e3f53ca Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 15:13:42 -0500 Subject: [PATCH 03/19] add tests --- .../managed/cdacreader/tests/GCCoverTests.cs | 105 ++++++++++++++++++ .../cdacreader/tests/MethodDescTests.cs | 3 + .../MockDescriptors.CodeVersions.cs | 4 +- .../MockDescriptors.GCCover.cs | 51 +++++++++ .../MockDescriptors.MethodDescriptors.cs | 1 + 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/native/managed/cdacreader/tests/GCCoverTests.cs create mode 100644 src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs diff --git a/src/native/managed/cdacreader/tests/GCCoverTests.cs b/src/native/managed/cdacreader/tests/GCCoverTests.cs new file mode 100644 index 00000000000000..11a000dc23ac8d --- /dev/null +++ b/src/native/managed/cdacreader/tests/GCCoverTests.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; +using Microsoft.Diagnostics.DataContractReader.Data; +using Moq; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +using MockGCCover = MockDescriptors.GCCover; + +public class GCCoverTests +{ + internal static Target CreateTarget( + MockTarget.Architecture arch, + MockGCCover builder, + Mock mockRuntimeTypeSystem = null) + { + TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types, builder.Globals); + + mockRuntimeTypeSystem ??= new Mock(); + + IContractFactory gcCoverFactory = new GCCoverFactory(); + + ContractRegistry reg = Mock.Of( + c => c.GCCover == gcCoverFactory.CreateContract(target, 1) + && c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object); + target.SetContracts(reg); + return target; + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetGCCoverageInfo_Explicit(MockTarget.Architecture arch) + { + GetGCCoverageInfoExplicitHelper(arch, new TargetPointer(0x1234_5678)); + GetGCCoverageInfoExplicitHelper(arch, TargetPointer.Null); + } + + private void GetGCCoverageInfoExplicitHelper(MockTarget.Architecture arch, TargetPointer expectedGCCoverageInfo) + { + MockGCCover mockGCCover = new(arch); + + NativeCodeVersionHandle codeVersionHandle = mockGCCover.AddExplicitNativeCodeVersion(expectedGCCoverageInfo); + + var target = CreateTarget(arch, mockGCCover); + + // TEST + + var gcCover = target.Contracts.GCCover; + Assert.NotNull(gcCover); + + TargetPointer? actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); + + Assert.NotNull(actualGCCoverageInfo); + Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetGCCoverageInfo_Synthetic(MockTarget.Architecture arch) + { + GetGCCoverageInfoSyntheticHelper(arch, new TargetPointer(0x1234_5678)); + GetGCCoverageInfoSyntheticHelper(arch, TargetPointer.Null); + } + + + private void GetGCCoverageInfoSyntheticHelper(MockTarget.Architecture arch, TargetPointer expectedGCCoverageInfo) + { + Mock mockRTS = new Mock(); + + TargetPointer methodDesc = new(/* arbitrary */ 0x1234_5678); + MockSyntheticGCCoverageInfo(mockRTS, methodDesc, expectedGCCoverageInfo); + + NativeCodeVersionHandle codeVersionHandle = NativeCodeVersionHandle.OfSynthetic(methodDesc); + + var target = CreateTarget(arch, new MockGCCover(arch), mockRTS); + + // TEST + + var gcCover = target.Contracts.GCCover; + Assert.NotNull(gcCover); + + TargetPointer? actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); + + Assert.NotNull(actualGCCoverageInfo); + Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); + } + + private void MockSyntheticGCCoverageInfo(Mock mockRTS, TargetPointer methodDescAddress, TargetPointer expectedGCCoverageInfo) + { + MethodDescHandle methodDescHandle = new MethodDescHandle(methodDescAddress); + mockRTS.Setup(rts => rts + .GetMethodDescHandle(methodDescAddress)) + .Returns(methodDescHandle); + mockRTS.Setup(rts => rts + .GetGCCoverageInfo(methodDescHandle)) + .Returns(expectedGCCoverageInfo); + } +} diff --git a/src/native/managed/cdacreader/tests/MethodDescTests.cs b/src/native/managed/cdacreader/tests/MethodDescTests.cs index 3157dcfaa535b6..848812bfdb0f41 100644 --- a/src/native/managed/cdacreader/tests/MethodDescTests.cs +++ b/src/native/managed/cdacreader/tests/MethodDescTests.cs @@ -69,6 +69,9 @@ public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch) Assert.False(isCollectible); TargetPointer versioning = rts.GetMethodDescVersioningState(handle); Assert.Equal(TargetPointer.Null, versioning); + TargetPointer? gcCoverageInfo = rts.GetGCCoverageInfo(handle); + Assert.NotNull(gcCoverageInfo); + Assert.Equal(TargetPointer.Null, gcCoverageInfo.Value); } public static IEnumerable StdArchOptionalSlotsData() diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index a4b2575eb2ac4c..d31fe8d5558e3e 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -34,6 +34,7 @@ public class CodeVersions new(nameof(Data.NativeCodeVersionNode.NativeCode), DataType.pointer), new(nameof(Data.NativeCodeVersionNode.Flags), DataType.uint32), new(nameof(Data.NativeCodeVersionNode.ILVersionId), DataType.nuint), + new(nameof(Data.NativeCodeVersionNode.GCCoverageInfo), DataType.pointer), ] }; @@ -116,7 +117,7 @@ public TargetPointer AddNativeCodeVersionNode() return fragment.Address; } - public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDesc, TargetCodePointer nativeCode, TargetPointer next, bool isActive, TargetNUInt ilVersionId) + public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDesc, TargetCodePointer nativeCode, TargetPointer next, bool isActive, TargetNUInt ilVersionId, TargetPointer? gcCoverageInfo = null) { Target.TypeInfo info = Types[DataType.NativeCodeVersionNode]; Span ncvn = Builder.BorrowAddressRange(dest, (int)info.Size!); @@ -125,6 +126,7 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe Builder.TargetTestHelpers.WritePointer(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.NativeCode)].Offset, Builder.TargetTestHelpers.PointerSize), nativeCode); Builder.TargetTestHelpers.Write(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.Flags)].Offset, sizeof(uint)), isActive ? (uint)CodeVersions_1.NativeCodeVersionNodeFlags.IsActiveChild : 0u); Builder.TargetTestHelpers.WriteNUInt(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.ILVersionId)].Offset, Builder.TargetTestHelpers.PointerSize), ilVersionId); + Builder.TargetTestHelpers.WritePointer(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.GCCoverageInfo)].Offset, Builder.TargetTestHelpers.PointerSize), gcCoverageInfo ?? TargetPointer.Null); } public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt ilVersion, TargetPointer? firstNode = null) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs new file mode 100644 index 00000000000000..9933e4b90b296f --- /dev/null +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +internal partial class MockDescriptors +{ + public class GCCover + { + private const ulong DefaultAllocationRangeStart = 0x0011_1000; + private const ulong DefaultAllocationRangeEnd = 0x00012_0000; + + internal readonly MockMemorySpace.Builder Builder; + + internal Dictionary Types { get; } + internal (string Name, ulong Value)[] Globals { get; } + + private CodeVersions _codeVersions { get; } + public GCCover(MockTarget.Architecture arch) + : this(new MockMemorySpace.Builder(new TargetTestHelpers(arch))) + { } + + public GCCover(MockMemorySpace.Builder builder) + { + Builder = builder; + + _codeVersions = new CodeVersions(Builder); + + Types = CodeVersions.GetTypes(builder.TargetTestHelpers); + } + + public NativeCodeVersionHandle AddExplicitNativeCodeVersion(TargetPointer gcCoverPointer) + { + TargetPointer nativeCodeVersionNode = _codeVersions.AddNativeCodeVersionNode(); + _codeVersions.FillNativeCodeVersionNode( + nativeCodeVersionNode, + TargetPointer.Null, + TargetCodePointer.Null, + TargetPointer.Null, + true, + new(1), + gcCoverPointer); + + return NativeCodeVersionHandle.OfExplicit(nativeCodeVersionNode); + } + } +} diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index 2e77d0238e0efb..27a371f1a27ca8 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -24,6 +24,7 @@ public class MethodDescriptors new(nameof(Data.MethodDesc.Flags3AndTokenRemainder), DataType.uint16), new(nameof(Data.MethodDesc.EntryPointFlags), DataType.uint8), new(nameof(Data.MethodDesc.CodeData), DataType.pointer), + new(nameof(Data.MethodDesc.GCCoverageInfo), DataType.pointer), ] }; From 8f629fd1e64b1562d840dc8237449b361cd71451 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Dec 2024 12:57:36 -0500 Subject: [PATCH 04/19] add GCCover contract docs --- docs/design/datacontracts/GCCover.md | 29 +++++++++++++++++++ .../design/datacontracts/RuntimeTypeSystem.md | 3 ++ 2 files changed, 32 insertions(+) create mode 100644 docs/design/datacontracts/GCCover.md diff --git a/docs/design/datacontracts/GCCover.md b/docs/design/datacontracts/GCCover.md new file mode 100644 index 00000000000000..1b3e0f91f2cd93 --- /dev/null +++ b/docs/design/datacontracts/GCCover.md @@ -0,0 +1,29 @@ +# Contract GCCover + +This contract encapsulates support for [GCCover](../coreclr/jit/investigate-stress.md) (GC stress testing) in the runtime. + +## APIs of contract + +```csharp +public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); +``` + +## Version 1 + +Data descriptors used: +| Data Descriptor Name | Field | Meaning | +| --- | --- | --- | +| NativeCodeVersionNode | GCCoverageInfo | pointer to GC coverage info | + +Contracts used: +| Contract Name | +| --- | +| CodeVersions | +| RuntimeTypeSystem | + +### Getting GCCoverageInfo for a NativeCodeVersion +```csharp +public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); +``` +1. If `codeVersionHandle` is synthetic, attempt to read the GCCoverageInfo off of the MethodDesc using the RuntimeTypeSystem contract. +2. If `codeVersionHandle` is explicit, fetch the `NativeCodeVersionNode` and attempt to read the `GCCoverageInfo` pointer. This value will only exist on targets with `HAVE_GCCOVER` enabled. If this value exists return it. Otherwise return null. diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 84726124483c73..e62be654bdc800 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -159,6 +159,9 @@ partial interface IRuntimeTypeSystem : IContract // Get an instruction pointer that can be called to cause the MethodDesc to be executed public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc); + // Gets the GCCoverageInfo pointer if available. + // If not enabled in the target process, returns null. + public virtual TargetPointer? GetGCCoverageInfo(MethodDescHandle methodDesc); } ``` From f1e9011c69816b88961481f266d66217c3e35b2f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Dec 2024 17:06:27 -0500 Subject: [PATCH 05/19] fix bugs --- src/coreclr/debug/runtimeinfo/datadescriptor.cpp | 4 ++++ src/coreclr/debug/runtimeinfo/datadescriptor.h | 3 ++- .../Contracts/IGCCover.cs | 2 ++ .../Data/MethodDesc.cs | 2 +- .../CachingContractRegistry.cs | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp index a6443a730985e0..72a1ee18304578 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp @@ -13,6 +13,10 @@ #include "methodtable.h" #include "threads.h" +#ifdef HAVE_GCCOVER +#include "gccover.h" +#endif // HAVE_GCCOVER + // begin blob definition extern "C" diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index e9874d9139971d..e9e2047d46656a 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -543,7 +543,8 @@ CDAC_TYPE_END(ProfControlBlock) #ifdef HAVE_GCCOVER CDAC_TYPE_BEGIN(GCCoverageInfo) -CDAC_TYPE_FIELD(GCCoverageInfo, /*pointer*/, SavedCode, offsetof(GCCoverageInfo, SavedCode)) +CDAC_TYPE_INDETERMINATE(GCCoverageInfo) +CDAC_TYPE_FIELD(GCCoverageInfo, /*pointer*/, SavedCode, offsetof(GCCoverageInfo, savedCode)) CDAC_TYPE_END(GCCoverageInfo) #endif // HAVE_GCCOVER diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs index 0f8c2f998b40ff..b1629d26865384 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs @@ -7,6 +7,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal interface IGCCover : IContract { + static string IContract.Name { get; } = nameof(GCCover); + public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs index 472b416835b0fc..0181d329a8413a 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDesc.cs @@ -20,7 +20,7 @@ public MethodDesc(Target target, TargetPointer address) CodeData = target.ReadPointer(address + (ulong)type.Fields[nameof(CodeData)].Offset); if (type.Fields.ContainsKey(nameof(GCCoverageInfo))) { - GCCoverageInfo = target.Read(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); + GCCoverageInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index 60727b072d2ae3..3696ed63c3ae1a 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -37,6 +37,7 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG [typeof(IPlatformMetadata)] = new PlatformMetadataFactory(), [typeof(IPrecodeStubs)] = new PrecodeStubsFactory(), [typeof(IReJIT)] = new ReJITFactory(), + [typeof(IGCCover)] = new GCCoverFactory(), }; configureFactories?.Invoke(_factories); } From 8526183a5141d43fd8daa6d60267f8fdfcb2538a Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 4 Dec 2024 11:36:13 -0500 Subject: [PATCH 06/19] fix merge issues --- .../Contracts/CodeVersions_1.cs | 2 -- src/native/managed/cdacreader/tests/GCCoverTests.cs | 3 +-- .../tests/MockDescriptors/MockDescriptors.GCCover.cs | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs index f6508aab8ecb76..e6b1755004c710 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs @@ -183,8 +183,6 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion }); } - - [Flags] internal enum MethodDescVersioningStateFlags : byte { diff --git a/src/native/managed/cdacreader/tests/GCCoverTests.cs b/src/native/managed/cdacreader/tests/GCCoverTests.cs index 11a000dc23ac8d..b60d4411f01040 100644 --- a/src/native/managed/cdacreader/tests/GCCoverTests.cs +++ b/src/native/managed/cdacreader/tests/GCCoverTests.cs @@ -69,7 +69,6 @@ public void GetGCCoverageInfo_Synthetic(MockTarget.Architecture arch) GetGCCoverageInfoSyntheticHelper(arch, TargetPointer.Null); } - private void GetGCCoverageInfoSyntheticHelper(MockTarget.Architecture arch, TargetPointer expectedGCCoverageInfo) { Mock mockRTS = new Mock(); @@ -77,7 +76,7 @@ private void GetGCCoverageInfoSyntheticHelper(MockTarget.Architecture arch, Targ TargetPointer methodDesc = new(/* arbitrary */ 0x1234_5678); MockSyntheticGCCoverageInfo(mockRTS, methodDesc, expectedGCCoverageInfo); - NativeCodeVersionHandle codeVersionHandle = NativeCodeVersionHandle.OfSynthetic(methodDesc); + NativeCodeVersionHandle codeVersionHandle = NativeCodeVersionHandle.CreateSynthetic(methodDesc); var target = CreateTarget(arch, new MockGCCover(arch), mockRTS); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs index 9933e4b90b296f..8c30d9bb34aefa 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs @@ -45,7 +45,7 @@ public NativeCodeVersionHandle AddExplicitNativeCodeVersion(TargetPointer gcCove new(1), gcCoverPointer); - return NativeCodeVersionHandle.OfExplicit(nativeCodeVersionNode); + return NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode); } } } From a0194d2fc3c67dec8324aa574c54c4ff1869c047 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Wed, 4 Dec 2024 12:05:30 -0500 Subject: [PATCH 07/19] remove unused fields --- .../tests/MockDescriptors/MockDescriptors.GCCover.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs index 8c30d9bb34aefa..f9b4d700136e78 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs @@ -11,9 +11,6 @@ internal partial class MockDescriptors { public class GCCover { - private const ulong DefaultAllocationRangeStart = 0x0011_1000; - private const ulong DefaultAllocationRangeEnd = 0x00012_0000; - internal readonly MockMemorySpace.Builder Builder; internal Dictionary Types { get; } From e2ecb757bd574d5b7a09148d60c3f519d1e58a77 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Thu, 5 Dec 2024 14:09:21 -0500 Subject: [PATCH 08/19] address comments --- docs/design/datacontracts/GCCover.md | 7 +++--- .../design/datacontracts/RuntimeTypeSystem.md | 5 ++-- .../Contracts/IGCCover.cs | 2 +- .../Contracts/IRuntimeTypeSystem.cs | 2 +- .../Contracts/GCCover_1.cs | 10 ++++---- .../Contracts/RuntimeTypeSystem_1.cs | 4 ++-- .../cdacreader/src/Legacy/SOSDacImpl.cs | 17 ++++++------- .../managed/cdacreader/tests/GCCoverTests.cs | 24 +++++++++---------- 8 files changed, 32 insertions(+), 39 deletions(-) diff --git a/docs/design/datacontracts/GCCover.md b/docs/design/datacontracts/GCCover.md index 1b3e0f91f2cd93..2e76239aeadc3b 100644 --- a/docs/design/datacontracts/GCCover.md +++ b/docs/design/datacontracts/GCCover.md @@ -5,7 +5,8 @@ This contract encapsulates support for [GCCover](../coreclr/jit/investigate-stre ## APIs of contract ```csharp -public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); +// Gets the GCCoverageInfo pointer if available, otherwise returns TargetPointer.Null +public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); ``` ## Version 1 @@ -23,7 +24,7 @@ Contracts used: ### Getting GCCoverageInfo for a NativeCodeVersion ```csharp -public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); +public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); ``` 1. If `codeVersionHandle` is synthetic, attempt to read the GCCoverageInfo off of the MethodDesc using the RuntimeTypeSystem contract. -2. If `codeVersionHandle` is explicit, fetch the `NativeCodeVersionNode` and attempt to read the `GCCoverageInfo` pointer. This value will only exist on targets with `HAVE_GCCOVER` enabled. If this value exists return it. Otherwise return null. +2. If `codeVersionHandle` is explicit, fetch the `NativeCodeVersionNode` and attempt to read the `GCCoverageInfo` pointer. This value will only exist on targets with `HAVE_GCCOVER` enabled. If this value exists return it. Otherwise return `TargetPointer.Null`. diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index e62be654bdc800..cf309a3f0c6b88 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -159,9 +159,8 @@ partial interface IRuntimeTypeSystem : IContract // Get an instruction pointer that can be called to cause the MethodDesc to be executed public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc); - // Gets the GCCoverageInfo pointer if available. - // If not enabled in the target process, returns null. - public virtual TargetPointer? GetGCCoverageInfo(MethodDescHandle methodDesc); + // Gets the GCCoverageInfo pointer if available, otherwise returns TargetPointer.Null + public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc); } ``` diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs index b1629d26865384..ac2966b044bc69 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs @@ -9,7 +9,7 @@ internal interface IGCCover : IContract { static string IContract.Name { get; } = nameof(GCCover); - public virtual TargetPointer? GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); } internal readonly struct GCCover : IGCCover diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index baa77fe29dd473..35782d10889cd9 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -167,7 +167,7 @@ internal interface IRuntimeTypeSystem : IContract public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException(); - public virtual TargetPointer? GetGCCoverageInfo(MethodDescHandle methodDesc) => throw new NotImplementedException(); + public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc) => throw new NotImplementedException(); #endregion MethodDesc inspection APIs } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs index 82eee70e62f8f7..caf35e68e7b256 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -15,12 +16,9 @@ internal GCCover_1(Target target) _target = target; } - TargetPointer? IGCCover.GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) + TargetPointer IGCCover.GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) { - if (!codeVersionHandle.Valid) - { - return null; - } + Debug.Assert(codeVersionHandle.Valid); if (!codeVersionHandle.IsExplicit) { @@ -33,7 +31,7 @@ internal GCCover_1(Target target) { // NativeCodeVersionNode::GetGCCoverageInfo NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle); - return codeVersionNode.GCCoverageInfo; + return codeVersionNode.GCCoverageInfo ?? TargetPointer.Null; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 8a2c4738954b97..ca24d3eb5f2cae 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1015,10 +1015,10 @@ private TargetCodePointer GetMethodEntryPointIfExists(MethodDesc md) return _target.ReadCodePointer(addrOfSlot); } - TargetPointer? IRuntimeTypeSystem.GetGCCoverageInfo(MethodDescHandle methodDesc) + TargetPointer IRuntimeTypeSystem.GetGCCoverageInfo(MethodDescHandle methodDesc) { MethodDesc md = _methodDescs[methodDesc.Address]; - return md.GCCoverageInfo; + return md.GCCoverageInfo ?? TargetPointer.Null; } private class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 6b54b325562498..1dd800b8d06d6d 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -352,17 +352,14 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes // HAVE_GCCOVER if (requestedNativeCodeVersion.Valid) { - TargetPointer? gcCoverAddr = _target.Contracts.GCCover.GetGCCoverageInfo(requestedNativeCodeVersion); - if (gcCoverAddr is not null) + IGCCover gcCover = _target.Contracts.GCCover; + TargetPointer gcCoverAddr = gcCover.GetGCCoverageInfo(requestedNativeCodeVersion); + if (gcCoverAddr.Value != TargetPointer.Null) { - // HAVE_GCCOVER is enabled - if (gcCoverAddr.Value != TargetPointer.Null) - { - // In certain minidumps, we won't save the gccover information. - // (it would be unwise to do so, it is heavy and not a customer scenario). - Target.TypeInfo gcCoverageInfo = _target.GetTypeInfo(DataType.GCCoverageInfo); - data->GCStressCodeCopy = gcCoverAddr.Value.Value + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset; - } + // In certain minidumps, we won't save the gccover information. + // (it would be unwise to do so, it is heavy and not a customer scenario). + Target.TypeInfo gcCoverageInfo = _target.GetTypeInfo(DataType.GCCoverageInfo); + data->GCStressCodeCopy = gcCoverAddr.Value + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset; } } diff --git a/src/native/managed/cdacreader/tests/GCCoverTests.cs b/src/native/managed/cdacreader/tests/GCCoverTests.cs index b60d4411f01040..5440017af4b53d 100644 --- a/src/native/managed/cdacreader/tests/GCCoverTests.cs +++ b/src/native/managed/cdacreader/tests/GCCoverTests.cs @@ -1,12 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using Microsoft.Diagnostics.DataContractReader.Contracts; -using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; -using Microsoft.Diagnostics.DataContractReader.Data; using Moq; using Xunit; @@ -21,7 +16,11 @@ internal static Target CreateTarget( MockGCCover builder, Mock mockRuntimeTypeSystem = null) { - TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types, builder.Globals); + TestPlaceholderTarget target = new TestPlaceholderTarget( + arch, + builder.Builder.GetReadContext().ReadFromTarget, + builder.Types, + builder.Globals); mockRuntimeTypeSystem ??= new Mock(); @@ -55,9 +54,7 @@ private void GetGCCoverageInfoExplicitHelper(MockTarget.Architecture arch, Targe var gcCover = target.Contracts.GCCover; Assert.NotNull(gcCover); - TargetPointer? actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); - - Assert.NotNull(actualGCCoverageInfo); + TargetPointer actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); } @@ -85,13 +82,14 @@ private void GetGCCoverageInfoSyntheticHelper(MockTarget.Architecture arch, Targ var gcCover = target.Contracts.GCCover; Assert.NotNull(gcCover); - TargetPointer? actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); - - Assert.NotNull(actualGCCoverageInfo); + TargetPointer actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); } - private void MockSyntheticGCCoverageInfo(Mock mockRTS, TargetPointer methodDescAddress, TargetPointer expectedGCCoverageInfo) + private void MockSyntheticGCCoverageInfo( + Mock mockRTS, + TargetPointer methodDescAddress, + TargetPointer expectedGCCoverageInfo) { MethodDescHandle methodDescHandle = new MethodDescHandle(methodDescAddress); mockRTS.Setup(rts => rts From 15ec95e94827bdfdff5e3ae7d693ba7c93060587 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 9 Dec 2024 14:21:17 -0500 Subject: [PATCH 09/19] address simple comments --- src/coreclr/vm/gccover.h | 6 ------ src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/coreclr/vm/gccover.h b/src/coreclr/vm/gccover.h index aca9f183657099..182a24bb975752 100644 --- a/src/coreclr/vm/gccover.h +++ b/src/coreclr/vm/gccover.h @@ -46,12 +46,6 @@ class GCCoverageInfo { void SprinkleBreakpoints(BYTE * saveAddr, PCODE codeStart, size_t codeSize, size_t regionOffsetAdj, BOOL fZapped); }; -template<> -struct cdac_data -{ - static constexpr size_t SavedCode = offsetof(GCCoverageInfo, savedCode); -}; - typedef DPTR(GCCoverageInfo) PTR_GCCoverageInfo; // see code:GCCoverageInfo::savedCode #ifdef _MSC_VER diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 1dd800b8d06d6d..3a35984dd9072c 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -354,7 +354,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes { IGCCover gcCover = _target.Contracts.GCCover; TargetPointer gcCoverAddr = gcCover.GetGCCoverageInfo(requestedNativeCodeVersion); - if (gcCoverAddr.Value != TargetPointer.Null) + if (gcCoverAddr != TargetPointer.Null) { // In certain minidumps, we won't save the gccover information. // (it would be unwise to do so, it is heavy and not a customer scenario). From 771949dcc5f998c9b0f97e81c1bc33db9238e442 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 11:15:20 -0500 Subject: [PATCH 10/19] remove GCCover contract and move API to CodeVersions --- docs/design/datacontracts/CodeVersions.md | 13 +++ .../design/datacontracts/RuntimeTypeSystem.md | 6 +- src/coreclr/debug/runtimeinfo/contracts.jsonc | 1 - .../debug/runtimeinfo/datadescriptor.h | 2 +- src/coreclr/vm/method.hpp | 3 - .../ContractRegistry.cs | 4 - .../Contracts/ICodeVersions.cs | 2 + .../Contracts/IGCCover.cs | 18 ---- .../Contracts/IRuntimeTypeSystem.cs | 2 +- .../Contracts/CodeVersions_1.cs | 24 +++++ .../Contracts/GCCoverFactory.cs | 18 ---- .../Contracts/GCCover_1.cs | 47 -------- .../Contracts/RuntimeTypeSystem_1.cs | 9 +- .../CachingContractRegistry.cs | 2 - .../cdacreader/src/Legacy/SOSDacImpl.cs | 12 +-- .../cdacreader/tests/CodeVersionsTests.cs | 79 ++++++++++++++ .../managed/cdacreader/tests/GCCoverTests.cs | 102 ------------------ .../MockDescriptors.CodeVersions.cs | 1 + .../MockDescriptors.GCCover.cs | 48 --------- .../MockDescriptors.RuntimeTypeSystem.cs | 1 + .../tests/MockDescriptors/MockDescriptors.cs | 9 ++ 21 files changed, 145 insertions(+), 258 deletions(-) delete mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs delete mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs delete mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs delete mode 100644 src/native/managed/cdacreader/tests/GCCoverTests.cs delete mode 100644 src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 27f0814c37dd34..b643107e7ff7fe 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -40,6 +40,9 @@ public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc); // Return the instruction pointer corresponding to the start of the given native code version public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle); + +// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null +public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle); ``` ### Extension Methods ```csharp @@ -61,6 +64,7 @@ Data descriptors used: | NativeCodeVersionNode | NativeCode | indicates an explicit native code version node | | NativeCodeVersionNode | Flags | `NativeCodeVersionNodeFlags` flags, see below | | NativeCodeVersionNode | VersionId | Version ID corresponding to the parent IL code version | +| NativeCodeVersionNode | GCCoverageInfo | GCStress debug info, if supported | | ILCodeVersioningState | FirstVersionNode | pointer to the first `ILCodeVersionNode` | | ILCodeVersioningState | ActiveVersionKind | an `ILCodeVersionKind` value indicating which fields of the active version are value | | ILCodeVersioningState | ActiveVersionNode | if the active version is explicit, the NativeCodeVersionNode for the active version | @@ -68,6 +72,7 @@ Data descriptors used: | ILCodeVersioningState | ActiveVersionMethodDef | if the active version is synthetic or unknown, the MethodDef token for the method | | ILCodeVersionNode | VersionId | Version ID of the node | | ILCodeVersionNode | Next | Pointer to the next `ILCodeVersionNode`| +| GCCoverageInfo | SavedCode | Pointer to the GCCover saved code copy | The flag indicates that the default version of the code for a method desc is active: ```csharp @@ -249,3 +254,11 @@ bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddr return true; } ``` + +### Finding GCStress Code Copy +```csharp +public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle); +``` + +1. If `codeVersionHandle` is synthetic, use the `IRuntimeTypeSystem` to find the GCStressCodeCopy. +2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value is nullable as it only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct. diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index cf309a3f0c6b88..4100bfb975752c 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -159,8 +159,8 @@ partial interface IRuntimeTypeSystem : IContract // Get an instruction pointer that can be called to cause the MethodDesc to be executed public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc); - // Gets the GCCoverageInfo pointer if available, otherwise returns TargetPointer.Null - public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc); + // Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null + public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc); } ``` @@ -640,6 +640,7 @@ We depend on the following data descriptors: | `MethodDesc` | `Slot` | The method's slot | | `MethodDesc` | `Flags` | The method's flags | | `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID | +| `MethodDesc` | `GCCoverageInfo` | The method's GCCover debug info, if supported | | `MethodDescCodeData` | `VersioningState` | The IL versioning state associated with a method descriptor | `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to | | `MethodDescChunk` | `Next` | The next chunk of methods | @@ -656,6 +657,7 @@ We depend on the following data descriptors: | `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature | | `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` | | `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc | +| `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy | The contract depends on the following other contracts diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc index 307586be5cd201..ae8cb684ab2c72 100644 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -14,7 +14,6 @@ "EcmaMetadata" : 1, "Exception": 1, "ExecutionManager": 2, - "GCCover": 1, "Loader": 1, "Object": 1, "PlatformMetadata": 1, diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index e9e2047d46656a..a07c490dfb2c0c 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -332,7 +332,7 @@ CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::EntryPointFlags) CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data::CodeData) #ifdef HAVE_GCCOVER -CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, cdac_data::GCCoverageInfo) +CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, MethodDesc::m_GcCover) #endif // HAVE_GCCOVER CDAC_TYPE_END(MethodDesc) diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index a5ea463409028e..44d03a2ffd3c16 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -1908,9 +1908,6 @@ template<> struct cdac_data static constexpr size_t Flags3AndTokenRemainder = offsetof(MethodDesc, m_wFlags3AndTokenRemainder); static constexpr size_t EntryPointFlags = offsetof(MethodDesc, m_bFlags4); static constexpr size_t CodeData = offsetof(MethodDesc, m_codeData); -#ifdef HAVE_GCCOVER - static constexpr size_t GCCoverageInfo = offsetof(MethodDesc, m_GcCover); -#endif // HAVE_GCCOVER }; #ifndef DACCESS_COMPILE diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index fc03f1f13ab40a..901084cd2c7d5e 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -59,8 +59,4 @@ internal abstract class ContractRegistry /// Gets an instance of the ReJIT contract for the target. /// public abstract IReJIT ReJIT { get; } - /// - /// Gets an instance of the GCCover contract for the target. - /// - public abstract IGCCover GCCover { get; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs index a6db692f7dede5..a422ccabfbd3e0 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs @@ -22,6 +22,8 @@ internal interface ICodeVersions : IContract public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc) => throw new NotImplementedException(); } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs deleted file mode 100644 index ac2966b044bc69..00000000000000 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IGCCover.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts; - -internal interface IGCCover : IContract -{ - static string IContract.Name { get; } = nameof(GCCover); - - public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); -} - -internal readonly struct GCCover : IGCCover -{ - // throws NotImplementedException for all methods -} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 35782d10889cd9..ecf243f9b6ce8a 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -167,7 +167,7 @@ internal interface IRuntimeTypeSystem : IContract public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException(); - public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc) => throw new NotImplementedException(); + public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc) => throw new NotImplementedException(); #endregion MethodDesc inspection APIs } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs index e6b1755004c710..6ec65adb9d34d1 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs @@ -183,6 +183,30 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion }); } + TargetPointer ICodeVersions.GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle) + { + Debug.Assert(codeVersionHandle.Valid); + + if (!codeVersionHandle.IsExplicit) + { + // NativeCodeVersion::GetGCCoverageInfo + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); + return rts.GetGCStressCodeCopy(md); + } + else + { + // NativeCodeVersionNode::GetGCCoverageInfo + NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle); + if (codeVersionNode.GCCoverageInfo is TargetPointer gcCoverageInfoAddr) + { + Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo); + return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset; + } + return TargetPointer.Null; + } + } + [Flags] internal enum MethodDescVersioningStateFlags : byte { diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs deleted file mode 100644 index 09b139cbe292bd..00000000000000 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCoverFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts; - -internal sealed class GCCoverFactory : IContractFactory -{ - IGCCover IContractFactory.CreateContract(Target target, int version) - { - return version switch - { - 1 => new GCCover_1(target), - _ => default(GCCover), - }; - } -} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs deleted file mode 100644 index caf35e68e7b256..00000000000000 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/GCCover_1.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using Microsoft.Diagnostics.DataContractReader.Data; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts; - -internal readonly struct GCCover_1 : IGCCover -{ - private readonly Target _target; - - internal GCCover_1(Target target) - { - _target = target; - } - - TargetPointer IGCCover.GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle) - { - Debug.Assert(codeVersionHandle.Valid); - - if (!codeVersionHandle.IsExplicit) - { - // NativeCodeVersion::GetGCCoverageInfo - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); - return rts.GetGCCoverageInfo(md); - } - else - { - // NativeCodeVersionNode::GetGCCoverageInfo - NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle); - return codeVersionNode.GCCoverageInfo ?? TargetPointer.Null; - } - } - - private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle) - { - if (handle.CodeVersionNodeAddress == TargetPointer.Null) - { - throw new InvalidOperationException("Synthetic NativeCodeVersion does not have a backing node."); - } - - return _target.ProcessedData.GetOrAdd(handle.CodeVersionNodeAddress); - } -} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index ca24d3eb5f2cae..a0d51753dd1a59 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1015,10 +1015,15 @@ private TargetCodePointer GetMethodEntryPointIfExists(MethodDesc md) return _target.ReadCodePointer(addrOfSlot); } - TargetPointer IRuntimeTypeSystem.GetGCCoverageInfo(MethodDescHandle methodDesc) + TargetPointer IRuntimeTypeSystem.GetGCStressCodeCopy(MethodDescHandle methodDesc) { MethodDesc md = _methodDescs[methodDesc.Address]; - return md.GCCoverageInfo ?? TargetPointer.Null; + if (md.GCCoverageInfo is TargetPointer gcCoverageInfoAddr) + { + Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo); + return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset; + } + return TargetPointer.Null; } private class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index 3696ed63c3ae1a..f2602ce5e1e6ca 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -37,7 +37,6 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG [typeof(IPlatformMetadata)] = new PlatformMetadataFactory(), [typeof(IPrecodeStubs)] = new PrecodeStubsFactory(), [typeof(IReJIT)] = new ReJITFactory(), - [typeof(IGCCover)] = new GCCoverFactory(), }; configureFactories?.Invoke(_factories); } @@ -54,7 +53,6 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG public override IPlatformMetadata PlatformMetadata => GetContract(); public override IPrecodeStubs PrecodeStubs => GetContract(); public override IReJIT ReJIT => GetContract(); - public override IGCCover GCCover => GetContract(); private TContract GetContract() where TContract : IContract { diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 3a35984dd9072c..4ca20ec761c698 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -352,15 +352,9 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes // HAVE_GCCOVER if (requestedNativeCodeVersion.Valid) { - IGCCover gcCover = _target.Contracts.GCCover; - TargetPointer gcCoverAddr = gcCover.GetGCCoverageInfo(requestedNativeCodeVersion); - if (gcCoverAddr != TargetPointer.Null) - { - // In certain minidumps, we won't save the gccover information. - // (it would be unwise to do so, it is heavy and not a customer scenario). - Target.TypeInfo gcCoverageInfo = _target.GetTypeInfo(DataType.GCCoverageInfo); - data->GCStressCodeCopy = gcCoverAddr.Value + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset; - } + // This is often TargetPointer.Null. In certain minidumps, we won't save the gccover information. + // (it would be unwise to do so, it is heavy and not a customer scenario). + data->GCInfo = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion); } if (data->bIsDynamic != 0) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index bdd6cacdd3a106..7a30e81ad99384 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -254,6 +254,24 @@ internal static Target CreateTarget( return target; } + internal static Target CreateTarget( + MockTarget.Architecture arch, + MockCodeVersions builder, + Mock mockRuntimeTypeSystem) + { + TestPlaceholderTarget target = new TestPlaceholderTarget( + arch, + builder.Builder.GetReadContext().ReadFromTarget, + builder.Types); + + IContractFactory cvfactory = new CodeVersionsFactory(); + ContractRegistry reg = Mock.Of( + c => c.CodeVersions == cvfactory.CreateContract(target, 1) + && c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object); + target.SetContracts(reg); + return target; + } + [Theory] [ClassData(typeof(MockTarget.StdArch))] public void GetNativeCodeVersion_Null(MockTarget.Architecture arch) @@ -678,4 +696,65 @@ public void IlToNativeToIlCodeVersion_SyntheticAndExplicit(MockTarget.Architectu NativeCodeVersionHandle syntheticNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion); Assert.True(syntheticILcodeVersion.Equals(codeVersions.GetILCodeVersion(syntheticNativeCodeVersion))); } + + private void GetGCStressCodeCopy_Impl(MockTarget.Architecture arch, bool returnsNull) + { + MockCodeVersions builder = new(arch); + Mock mockRTS = new(); + + // Setup synthetic NativeCodeVersion + TargetPointer expectedSyntheticCodeCopyAddr = returnsNull ? TargetPointer.Null : new(0x2345_6789); + TargetPointer syntheticMethodDescAddr = new(0x2345_8000); + NativeCodeVersionHandle syntheticHandle = NativeCodeVersionHandle.CreateSynthetic(syntheticMethodDescAddr); + MethodDescHandle methodDescHandle = new MethodDescHandle(syntheticMethodDescAddr); + mockRTS.Setup(rts => rts.GetMethodDescHandle(syntheticMethodDescAddr)).Returns(methodDescHandle); + mockRTS.Setup(rts => rts.GetGCStressCodeCopy(methodDescHandle)).Returns(expectedSyntheticCodeCopyAddr); + + // Setup explicit NativeCodeVersion + TargetPointer? explicitGCCoverageInfoAddr = returnsNull ? TargetPointer.Null : new(0x1234_5678); + TargetPointer nativeCodeVersionNode = builder.AddNativeCodeVersionNode(); + builder.FillNativeCodeVersionNode( + nativeCodeVersionNode, + methodDesc: TargetPointer.Null, + nativeCode: TargetCodePointer.Null, + next: TargetPointer.Null, + isActive: true, + ilVersionId: new(1), + gcCoverageInfo: explicitGCCoverageInfoAddr); + NativeCodeVersionHandle explicitHandle = NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode); + + var target = CreateTarget(arch, builder, mockRTS); + var codeVersions = target.Contracts.CodeVersions; + + // TEST + TargetPointer actualSyntheticCodeCopyAddr = codeVersions.GetGCStressCodeCopy(syntheticHandle); + Assert.Equal(expectedSyntheticCodeCopyAddr, actualSyntheticCodeCopyAddr); + + if(returnsNull) + { + TargetPointer actualExplicitCodeCopyAddr = codeVersions.GetGCStressCodeCopy(explicitHandle); + Assert.Equal(TargetPointer.Null, actualExplicitCodeCopyAddr); + } + else + { + Target.TypeInfo gcCoverageInfoType = target.GetTypeInfo(DataType.GCCoverageInfo); + TargetPointer expectedExplicitCodeCopyAddr = explicitGCCoverageInfoAddr.Value + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset; + TargetPointer actualExplicitCodeCopyAddr = codeVersions.GetGCStressCodeCopy(explicitHandle); + Assert.Equal(expectedExplicitCodeCopyAddr, actualExplicitCodeCopyAddr); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetGCStressCodeCopy_Null(MockTarget.Architecture arch) + { + GetGCStressCodeCopy_Impl(arch, returnsNull: true); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetGCStressCodeCopy_NotNull(MockTarget.Architecture arch) + { + GetGCStressCodeCopy_Impl(arch, returnsNull: false); + } } diff --git a/src/native/managed/cdacreader/tests/GCCoverTests.cs b/src/native/managed/cdacreader/tests/GCCoverTests.cs deleted file mode 100644 index 5440017af4b53d..00000000000000 --- a/src/native/managed/cdacreader/tests/GCCoverTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Diagnostics.DataContractReader.Contracts; -using Moq; -using Xunit; - -namespace Microsoft.Diagnostics.DataContractReader.Tests; - -using MockGCCover = MockDescriptors.GCCover; - -public class GCCoverTests -{ - internal static Target CreateTarget( - MockTarget.Architecture arch, - MockGCCover builder, - Mock mockRuntimeTypeSystem = null) - { - TestPlaceholderTarget target = new TestPlaceholderTarget( - arch, - builder.Builder.GetReadContext().ReadFromTarget, - builder.Types, - builder.Globals); - - mockRuntimeTypeSystem ??= new Mock(); - - IContractFactory gcCoverFactory = new GCCoverFactory(); - - ContractRegistry reg = Mock.Of( - c => c.GCCover == gcCoverFactory.CreateContract(target, 1) - && c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object); - target.SetContracts(reg); - return target; - } - - [Theory] - [ClassData(typeof(MockTarget.StdArch))] - public void GetGCCoverageInfo_Explicit(MockTarget.Architecture arch) - { - GetGCCoverageInfoExplicitHelper(arch, new TargetPointer(0x1234_5678)); - GetGCCoverageInfoExplicitHelper(arch, TargetPointer.Null); - } - - private void GetGCCoverageInfoExplicitHelper(MockTarget.Architecture arch, TargetPointer expectedGCCoverageInfo) - { - MockGCCover mockGCCover = new(arch); - - NativeCodeVersionHandle codeVersionHandle = mockGCCover.AddExplicitNativeCodeVersion(expectedGCCoverageInfo); - - var target = CreateTarget(arch, mockGCCover); - - // TEST - - var gcCover = target.Contracts.GCCover; - Assert.NotNull(gcCover); - - TargetPointer actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); - Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); - } - - [Theory] - [ClassData(typeof(MockTarget.StdArch))] - public void GetGCCoverageInfo_Synthetic(MockTarget.Architecture arch) - { - GetGCCoverageInfoSyntheticHelper(arch, new TargetPointer(0x1234_5678)); - GetGCCoverageInfoSyntheticHelper(arch, TargetPointer.Null); - } - - private void GetGCCoverageInfoSyntheticHelper(MockTarget.Architecture arch, TargetPointer expectedGCCoverageInfo) - { - Mock mockRTS = new Mock(); - - TargetPointer methodDesc = new(/* arbitrary */ 0x1234_5678); - MockSyntheticGCCoverageInfo(mockRTS, methodDesc, expectedGCCoverageInfo); - - NativeCodeVersionHandle codeVersionHandle = NativeCodeVersionHandle.CreateSynthetic(methodDesc); - - var target = CreateTarget(arch, new MockGCCover(arch), mockRTS); - - // TEST - - var gcCover = target.Contracts.GCCover; - Assert.NotNull(gcCover); - - TargetPointer actualGCCoverageInfo = gcCover.GetGCCoverageInfo(codeVersionHandle); - Assert.Equal(expectedGCCoverageInfo, actualGCCoverageInfo); - } - - private void MockSyntheticGCCoverageInfo( - Mock mockRTS, - TargetPointer methodDescAddress, - TargetPointer expectedGCCoverageInfo) - { - MethodDescHandle methodDescHandle = new MethodDescHandle(methodDescAddress); - mockRTS.Setup(rts => rts - .GetMethodDescHandle(methodDescAddress)) - .Returns(methodDescHandle); - mockRTS.Setup(rts => rts - .GetGCCoverageInfo(methodDescHandle)) - .Returns(expectedGCCoverageInfo); - } -} diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index d31fe8d5558e3e..bdfa03acefb2e6 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -95,6 +95,7 @@ public CodeVersions(MockMemorySpace.Builder builder, (ulong Start, ulong End) al NativeCodeVersionNodeFields, ILCodeVersioningStateFields, ILCodeVersionNodeFields, + GCCoverageInfoFields, ]); } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs deleted file mode 100644 index f9b4d700136e78..00000000000000 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.GCCover.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using Microsoft.Diagnostics.DataContractReader.Contracts; - -namespace Microsoft.Diagnostics.DataContractReader.Tests; - -internal partial class MockDescriptors -{ - public class GCCover - { - internal readonly MockMemorySpace.Builder Builder; - - internal Dictionary Types { get; } - internal (string Name, ulong Value)[] Globals { get; } - - private CodeVersions _codeVersions { get; } - public GCCover(MockTarget.Architecture arch) - : this(new MockMemorySpace.Builder(new TargetTestHelpers(arch))) - { } - - public GCCover(MockMemorySpace.Builder builder) - { - Builder = builder; - - _codeVersions = new CodeVersions(Builder); - - Types = CodeVersions.GetTypes(builder.TargetTestHelpers); - } - - public NativeCodeVersionHandle AddExplicitNativeCodeVersion(TargetPointer gcCoverPointer) - { - TargetPointer nativeCodeVersionNode = _codeVersions.AddNativeCodeVersionNode(); - _codeVersions.FillNativeCodeVersionNode( - nativeCodeVersionNode, - TargetPointer.Null, - TargetCodePointer.Null, - TargetPointer.Null, - true, - new(1), - gcCoverPointer); - - return NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode); - } - } -} diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs index bade23b8cb390a..8034cf22f70437 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs @@ -72,6 +72,7 @@ public class RuntimeTypeSystem FnPtrTypeDescFields, ParamTypeDescFields, TypeVarTypeDescFields, + GCCoverageInfoFields, ]); } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs index 50d3c76135c768..30336dd876f6a7 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs @@ -198,6 +198,15 @@ internal record TypeFields ] }; + private static readonly TypeFields GCCoverageInfoFields = new TypeFields() + { + DataType = DataType.GCCoverageInfo, + Fields = + [ + new("SavedCode", DataType.pointer), + ] + }; + internal static Dictionary GetTypesForTypeFields(TargetTestHelpers helpers, TypeFields[] typeFields) { Dictionary types = new(); From 635a966a66a9bb948f61655603f1a16db50ca9e9 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 11:40:40 -0500 Subject: [PATCH 11/19] fix merge issue --- src/native/managed/cdacreader/tests/MethodDescTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodDescTests.cs b/src/native/managed/cdacreader/tests/MethodDescTests.cs index 848812bfdb0f41..abe05363822897 100644 --- a/src/native/managed/cdacreader/tests/MethodDescTests.cs +++ b/src/native/managed/cdacreader/tests/MethodDescTests.cs @@ -69,9 +69,8 @@ public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch) Assert.False(isCollectible); TargetPointer versioning = rts.GetMethodDescVersioningState(handle); Assert.Equal(TargetPointer.Null, versioning); - TargetPointer? gcCoverageInfo = rts.GetGCCoverageInfo(handle); - Assert.NotNull(gcCoverageInfo); - Assert.Equal(TargetPointer.Null, gcCoverageInfo.Value); + TargetPointer gcStressCodeCopy = rts.GetGCStressCodeCopy(handle); + Assert.Equal(TargetPointer.Null, gcStressCodeCopy); } public static IEnumerable StdArchOptionalSlotsData() From 2396d068caf5fbc037b10fa13ca5e8e066b8a220 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 11:41:50 -0500 Subject: [PATCH 12/19] delete gccover.md --- docs/design/datacontracts/GCCover.md | 30 ---------------------------- 1 file changed, 30 deletions(-) delete mode 100644 docs/design/datacontracts/GCCover.md diff --git a/docs/design/datacontracts/GCCover.md b/docs/design/datacontracts/GCCover.md deleted file mode 100644 index 2e76239aeadc3b..00000000000000 --- a/docs/design/datacontracts/GCCover.md +++ /dev/null @@ -1,30 +0,0 @@ -# Contract GCCover - -This contract encapsulates support for [GCCover](../coreclr/jit/investigate-stress.md) (GC stress testing) in the runtime. - -## APIs of contract - -```csharp -// Gets the GCCoverageInfo pointer if available, otherwise returns TargetPointer.Null -public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); -``` - -## Version 1 - -Data descriptors used: -| Data Descriptor Name | Field | Meaning | -| --- | --- | --- | -| NativeCodeVersionNode | GCCoverageInfo | pointer to GC coverage info | - -Contracts used: -| Contract Name | -| --- | -| CodeVersions | -| RuntimeTypeSystem | - -### Getting GCCoverageInfo for a NativeCodeVersion -```csharp -public virtual TargetPointer GetGCCoverageInfo(NativeCodeVersionHandle codeVersionHandle); -``` -1. If `codeVersionHandle` is synthetic, attempt to read the GCCoverageInfo off of the MethodDesc using the RuntimeTypeSystem contract. -2. If `codeVersionHandle` is explicit, fetch the `NativeCodeVersionNode` and attempt to read the `GCCoverageInfo` pointer. This value will only exist on targets with `HAVE_GCCOVER` enabled. If this value exists return it. Otherwise return `TargetPointer.Null`. From 61dde8068eeac1e599c20f7f61e2ac9589edefde Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 11:43:45 -0500 Subject: [PATCH 13/19] improve doc --- docs/design/datacontracts/CodeVersions.md | 2 +- docs/design/datacontracts/RuntimeTypeSystem.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index b643107e7ff7fe..57cc1ac76ea38f 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -72,7 +72,7 @@ Data descriptors used: | ILCodeVersioningState | ActiveVersionMethodDef | if the active version is synthetic or unknown, the MethodDef token for the method | | ILCodeVersionNode | VersionId | Version ID of the node | | ILCodeVersionNode | Next | Pointer to the next `ILCodeVersionNode`| -| GCCoverageInfo | SavedCode | Pointer to the GCCover saved code copy | +| GCCoverageInfo | SavedCode | Pointer to the GCCover saved code copy, if supported | The flag indicates that the default version of the code for a method desc is active: ```csharp diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 4100bfb975752c..b1d65b7c825273 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -657,7 +657,7 @@ We depend on the following data descriptors: | `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature | | `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` | | `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc | -| `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy | +| `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy, if supported | The contract depends on the following other contracts From 90584bfbe31795614997e1a5acd00b44109ce972 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 11:47:44 -0500 Subject: [PATCH 14/19] improve comments --- src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 4ca20ec761c698..0540e3ef7c6ec1 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -352,7 +352,8 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes // HAVE_GCCOVER if (requestedNativeCodeVersion.Valid) { - // This is often TargetPointer.Null. In certain minidumps, we won't save the gccover information. + // TargetPointer.Null if GCCover information is not available. + // In certain minidumps, we won't save the GCCover information. // (it would be unwise to do so, it is heavy and not a customer scenario). data->GCInfo = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion); } From 090adca458fee89f916d5078cea70a242648b781 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 14:05:07 -0500 Subject: [PATCH 15/19] fix datadescriptor --- src/coreclr/debug/runtimeinfo/datadescriptor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index a07c490dfb2c0c..0e6586ec44a29d 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -332,7 +332,7 @@ CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::EntryPointFlags) CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data::CodeData) #ifdef HAVE_GCCOVER -CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, MethodDesc::m_GcCover) +CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, offsetof(MethodDesc, m_GcCover)) #endif // HAVE_GCCOVER CDAC_TYPE_END(MethodDesc) From 991b217347ad2e199a8d5529aabd2efb8f622633 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 17:02:23 -0500 Subject: [PATCH 16/19] fix gcstresscode --- src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 0540e3ef7c6ec1..5962164314b060 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -355,7 +355,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes // TargetPointer.Null if GCCover information is not available. // In certain minidumps, we won't save the GCCover information. // (it would be unwise to do so, it is heavy and not a customer scenario). - data->GCInfo = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion); + data->GCStressCodeCopy = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion); } if (data->bIsDynamic != 0) From cf7a5f111afc33aff55cda27192a6a2eebda3880 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 17:30:40 -0500 Subject: [PATCH 17/19] fix null check --- .../Contracts/CodeVersions_1.cs | 2 +- .../Contracts/RuntimeTypeSystem_1.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs index 6ec65adb9d34d1..70fec695684029 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs @@ -198,7 +198,7 @@ TargetPointer ICodeVersions.GetGCStressCodeCopy(NativeCodeVersionHandle codeVers { // NativeCodeVersionNode::GetGCCoverageInfo NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle); - if (codeVersionNode.GCCoverageInfo is TargetPointer gcCoverageInfoAddr) + if (codeVersionNode.GCCoverageInfo is TargetPointer gcCoverageInfoAddr && gcCoverageInfoAddr != TargetPointer.Null) { Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo); return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset; diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index a0d51753dd1a59..d32fae6d4b07c7 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1018,7 +1018,7 @@ private TargetCodePointer GetMethodEntryPointIfExists(MethodDesc md) TargetPointer IRuntimeTypeSystem.GetGCStressCodeCopy(MethodDescHandle methodDesc) { MethodDesc md = _methodDescs[methodDesc.Address]; - if (md.GCCoverageInfo is TargetPointer gcCoverageInfoAddr) + if (md.GCCoverageInfo is TargetPointer gcCoverageInfoAddr && gcCoverageInfoAddr != TargetPointer.Null) { Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo); return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset; From a6eefcb70ba69f7ab647c69a8f6e8ae35ddc40a1 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 10 Dec 2024 17:49:24 -0500 Subject: [PATCH 18/19] improve test to differentiate if SavedCode offset is added --- .../managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs index 30336dd876f6a7..23444bb58d55ba 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs @@ -203,6 +203,8 @@ internal record TypeFields DataType = DataType.GCCoverageInfo, Fields = [ + // Add DummyField to ensure the offset of SavedCode is not added to the TargetPointer.Null + new("DummyField", DataType.pointer), new("SavedCode", DataType.pointer), ] }; From c0bd0ea30441f97802d5e1fa6e031fac03c0d0db Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:17:40 -0500 Subject: [PATCH 19/19] Update docs/design/datacontracts/CodeVersions.md Co-authored-by: Elinor Fung --- docs/design/datacontracts/CodeVersions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 57cc1ac76ea38f..c903bd84007cbc 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -261,4 +261,4 @@ public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVer ``` 1. If `codeVersionHandle` is synthetic, use the `IRuntimeTypeSystem` to find the GCStressCodeCopy. -2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value is nullable as it only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct. +2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct.