From d4de7c9dc907f8f16f889590640faa3e614121c2 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Fri, 22 Nov 2024 15:02:07 -0500 Subject: [PATCH 01/11] add rejit support to GetMethodDescData --- docs/design/datacontracts/CodeVersions.md | 72 ++++++- docs/design/datacontracts/ReJIT.md | 84 +++++++++ .../debug/runtimeinfo/datadescriptor.h | 3 + src/coreclr/vm/codeversion.h | 3 + .../Extensions/ICodeVersionsExtensions.cs | 13 ++ .../Contracts/Extensions/IReJITExtensions.cs | 24 +++ .../Contracts/ICodeVersions.cs | 34 +++- .../Contracts/IReJIT.cs | 12 ++ .../TargetNUInt.cs | 12 +- .../Contracts/CodeVersions_1.cs | 176 +++++++++++++----- .../Contracts/ReJIT_1.cs | 54 ++++++ .../Data/ILCodeVersionNode.cs | 6 + .../Data/ILCodeVersioningState.cs | 2 + .../cdacreader/src/Legacy/SOSDacImpl.cs | 162 ++++++++++++++-- .../cdacreader/tests/CodeVersionsTests.cs | 158 +++++++++++++++- .../MockDescriptors.CodeVersions.cs | 30 ++- .../MockDescriptors/MockDescriptors.ReJIT.cs | 101 ++++++++++ .../managed/cdacreader/tests/ReJITTests.cs | 131 +++++++++++++ 18 files changed, 1007 insertions(+), 70 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IReJITExtensions.cs create mode 100644 src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs create mode 100644 src/native/managed/cdacreader/tests/ReJITTests.cs diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 19cee2b20730c..7bab465b0a761 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -4,6 +4,29 @@ This contract encapsulates support for [code versioning](../features/code-versio ## APIs of contract +```csharp +internal readonly struct ILCodeVersionHandle +{ + internal readonly TargetPointer Module; + internal readonly uint MethodDefinition; + internal readonly TargetPointer ILCodeVersionNode; + internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) + { + if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null) + throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null"); + + if (module != TargetPointer.Null && methodDef == 0) + throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null"); + + Module = module; + MethodDefinition = methodDef; + ILCodeVersionNode = ilCodeVersionNodeAddress; + } + public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; +} +``` + ```csharp internal struct NativeCodeVersionHandle { @@ -26,10 +49,19 @@ internal struct NativeCodeVersionHandle ``` ```csharp +// Return a handle to the active version of the IL code for a given method descriptor +public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc); +// Return a handle to the IL code version representing the given native code version +public virtual ILCodeVersionHandle GetILCodeVersion(NativeCodeVersionHandle codeVersionHandle); +// Return all of the IL code versions for a given method descriptor +public virtual IEnumerable GetILCodeVersions(TargetPointer methodDesc); + // Return a handle to the version of the native code that includes the given instruction pointer public virtual NativeCodeVersionHandle GetNativeCodeVersionForIP(TargetCodePointer ip); // Return a handle to the active version of the native code for a given method descriptor public virtual NativeCodeVersionHandle GetActiveNativeCodeVersion(TargetPointer methodDesc); +// Return a handle to the active version of the native code for a given method descriptor and IL code version. The IL code version and method descriptor must represent the same method +public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle); // returns true if the given method descriptor supports multiple code versions public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc); @@ -52,11 +84,13 @@ 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 | +| 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 | | ILCodeVersioningState | ActiveVersionModule | if the active version is synthetic or unknown, the pointer to the Module that defines the method | | 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`| The flag indicates that the default version of the code for a method desc is active: ```csharp @@ -93,6 +127,40 @@ Contracts used: | Loader | | RuntimeTypeSystem | +### Finding all of the ILCodeVersions for a method +```csharp +IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer methodDesc) +{ + // CodeVersionManager::GetILCodeVersions + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer module = rts.GetModule(typeHandle); + uint methodDefToken = rts.GetMethodToken(md); + + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); + TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; + TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); + + // always add the synthetic version + yield return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + + // if explicit versions exist, iterate linked list and return them + if (ilVersionStateAddress != TargetPointer.Null) + { + Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); + TargetPointer nodePointer = ilState.FirstVersionNode; + while (nodePointer != TargetPointer.Null) + { + Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd(nodePointer); + yield return new ILCodeVersionHandle(TargetPointer.Null, 0, nodePointer); + nodePointer = current.Next; + } + } +} +``` + ### Finding the start of a specific native code version ```csharp @@ -235,7 +303,7 @@ bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion) NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress) { - TargetNUInt? ilVersionId = default; + TargetNUInt ilVersionId = GetId(ilcodeVersion); if (methodDefActiveVersion.Module != TargetPointer.Null) { NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); @@ -256,7 +324,7 @@ NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDe MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); return FindFirstCodeVersion(rts, md, (codeVersion) => { - return (!ilVersionId.HasValue || ilVersionId.Value.Value == codeVersion.ILVersionId.Value) + return (ilVersionId == codeVersion.ILVersionId) && ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); }); } diff --git a/docs/design/datacontracts/ReJIT.md b/docs/design/datacontracts/ReJIT.md index 53ed0767a6a9d..787db4e00f4bd 100644 --- a/docs/design/datacontracts/ReJIT.md +++ b/docs/design/datacontracts/ReJIT.md @@ -4,8 +4,22 @@ This contract encapsulates support for [ReJIT](../features/code-versioning.md) i ## APIs of contract +```csharp +public enum RejitState +{ + Requested, + Active +} +``` + ```csharp bool IsEnabled(); + +RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle); + +TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle); + +IEnumerable GetRejitIds(TargetPointer methodDesc) ``` ## Version 1 @@ -14,6 +28,8 @@ Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | | ProfControlBlock | GlobalEventMask | an `ICorProfiler` `COR_PRF_MONITOR` value | +| ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT id +| ILCodeVersionNode | RejitState | a `RejitFlags` value | Global variables used: | Global Name | Type | Purpose | @@ -23,6 +39,7 @@ Global variables used: Contracts used: | Contract Name | | --- | +| CodeVersions | ```csharp // see src/coreclr/inc/corprof.idl @@ -32,6 +49,21 @@ private enum COR_PRF_MONITOR COR_PRF_ENABLE_REJIT = 0x00040000, } +// see src/coreclr/vm/codeversion.h +[Flags] +public enum RejitFlags : uint +{ + kStateRequested = 0x00000000, + + kStateGettingReJITParameters = 0x00000001, + + kStateActive = 0x00000002, + + kStateMask = 0x0000000F, + + kSuppressParams = 0x80000000 +} + bool IsEnabled() { TargetPointer address = target.ReadGlobalPointer("ProfilerControlBlock"); @@ -40,4 +72,56 @@ bool IsEnabled() bool clrConfigEnabledReJit = /* host process does not have environment variable DOTNET_ProfAPI_ReJitOnAttach set to 0 */; return profEnabledReJIT || clrConfigEnabledReJIT; } + +RejitState GetRejitState(ILCodeVersionHandle codeVersion) +{ + // ILCodeVersion::GetRejitState + if (codeVersion is not explicit) + { + // for non explicit ILCodeVersions, ReJITState is always kStateActive + return RejitState.Active; + } + else + { + // ILCodeVersionNode::GetRejitState + ILCodeVersionNode codeVersionNode = AsNode(codeVersion); + return ((RejitFlags)ilCodeVersionNode.RejitState & RejitFlags.kStateMask) switch + { + RejitFlags.kStateRequested => RejitState.Requested, + RejitFlags.kStateActive => RejitState.Active, + _ => throw new NotImplementedException($"Unknown ReJIT state: {ilCodeVersionNode.RejitState}"), + }; + } +} + +TargetNUInt GetRejitId(ILCodeVersionHandle codeVersion) +{ + // ILCodeVersion::GetVersionId + if (codeVersion is not explicit) + { + // for non explicit ILCodeVersions, ReJITId is always 0 + return new TargetNUInt(0); + } + else + { + // ILCodeVersionNode::GetVersionId + ILCodeVersionNode codeVersionNode = AsNode(codeVersion); + return codeVersionNode.VersionId; + } +} + +IEnumerable GetRejitIds(TargetPointer methodDesc) +{ + // ReJitManager::GetReJITIDs + ICodeVersions cv = _target.Contracts.CodeVersions; + IEnumerable ilCodeVersions = cv.GetILCodeVersions(methodDesc); + + foreach (ILCodeVersionHandle ilCodeVersionHandle in ilCodeVersions) + { + if (GetRejitState(ilCodeVersionHandle) == RejitState.Active) + { + yield return GetRejitId(ilCodeVersionHandle); + } + } +} ``` diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 4568395df3cac..3aef2fc875aa6 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -483,6 +483,7 @@ CDAC_TYPE_END(CodeHeapListNode) CDAC_TYPE_BEGIN(ILCodeVersioningState) CDAC_TYPE_INDETERMINATE(ILCodeVersioningState) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, FirstVersionNode, cdac_data::FirstVersionNode) CDAC_TYPE_FIELD(ILCodeVersioningState, /*uint32*/, ActiveVersionKind, cdac_data::ActiveVersionKind) CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionNode, cdac_data::ActiveVersionNode) CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionModule, cdac_data::ActiveVersionModule) @@ -501,6 +502,8 @@ CDAC_TYPE_END(NativeCodeVersionNode) CDAC_TYPE_BEGIN(ILCodeVersionNode) CDAC_TYPE_INDETERMINATE(ILCodeVersionNode) CDAC_TYPE_FIELD(ILCodeVersionNode, /*nuint*/, VersionId, cdac_data::VersionId) +CDAC_TYPE_FIELD(ILCodeVersionNode, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(ILCodeVersionNode, /*uint32*/, RejitState, cdac_data::RejitState) CDAC_TYPE_END(ILCodeVersionNode) CDAC_TYPE_BEGIN(ProfControlBlock) diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index c7687d0746c53..442a284654375 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -419,6 +419,8 @@ template<> struct cdac_data { static constexpr size_t VersionId = offsetof(ILCodeVersionNode, m_rejitId); + static constexpr size_t Next = offsetof(ILCodeVersionNode, m_pNextILVersionNode); + static constexpr size_t RejitState = offsetof(ILCodeVersionNode, m_rejitState); }; class ILCodeVersionCollection @@ -543,6 +545,7 @@ class ILCodeVersioningState template<> struct cdac_data { + static constexpr size_t FirstVersionNode = offsetof(ILCodeVersioningState, m_pFirstVersionNode); static constexpr size_t ActiveVersionKind = offsetof(ILCodeVersioningState, m_activeVersion.m_storageKind); static constexpr size_t ActiveVersionNode = offsetof(ILCodeVersioningState, m_activeVersion.m_pVersionNode); static constexpr size_t ActiveVersionModule = offsetof(ILCodeVersioningState, m_activeVersion.m_synthetic.m_pModule); diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs new file mode 100644 index 0000000000000..157923d5d999e --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; + +internal static class ICodeVersionsExtensions +{ + internal static NativeCodeVersionHandle GetActiveNativeCodeVersion(this ICodeVersions cv, TargetPointer methodDesc) + { + ILCodeVersionHandle iLCodeVersionHandle = cv.GetActiveILCodeVersion(methodDesc); + return cv.GetActiveNativeCodeVersionForILCodeVersion(methodDesc, iLCodeVersionHandle); + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IReJITExtensions.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IReJITExtensions.cs new file mode 100644 index 0000000000000..be505aa3d662f --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/IReJITExtensions.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; + +internal static class IReJITExtensions +{ + public static IEnumerable GetRejitIds(this IReJIT rejit, Target target, TargetPointer methodDesc) + { + ICodeVersions cv = target.Contracts.CodeVersions; + + IEnumerable ilCodeVersions = cv.GetILCodeVersions(methodDesc); + + foreach (ILCodeVersionHandle ilCodeVersionHandle in ilCodeVersions) + { + if (rejit.GetRejitState(ilCodeVersionHandle) == RejitState.Active) + { + yield return rejit.GetRejitId(ilCodeVersionHandle); + } + } + } +} 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 bddba2cdc2787..9fcc3c0250daf 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -9,16 +10,43 @@ internal interface ICodeVersions : IContract { static string IContract.Name { get; } = nameof(CodeVersions); + public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc) => throw new NotImplementedException(); + + public virtual ILCodeVersionHandle GetILCodeVersion(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + + public virtual IEnumerable GetILCodeVersions(TargetPointer methodDesc) => throw new NotImplementedException(); + public virtual NativeCodeVersionHandle GetNativeCodeVersionForIP(TargetCodePointer ip) => throw new NotImplementedException(); - public virtual NativeCodeVersionHandle GetActiveNativeCodeVersion(TargetPointer methodDesc) => throw new NotImplementedException(); - public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc) => throw new NotImplementedException(); + public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle) => throw new NotImplementedException(); public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc) => throw new NotImplementedException(); +} + +internal readonly struct ILCodeVersionHandle +{ + internal readonly TargetPointer Module; + internal readonly uint MethodDefinition; + internal readonly TargetPointer ILCodeVersionNode; + internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) + { + if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null) + throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null"); + + if (module != TargetPointer.Null && methodDef == 0) + throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null"); + + Module = module; + MethodDefinition = methodDef; + ILCodeVersionNode = ilCodeVersionNodeAddress; + } + public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; } -internal struct NativeCodeVersionHandle +internal readonly struct NativeCodeVersionHandle { // no public constructors internal readonly TargetPointer MethodDescAddress; diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs index 6bb9f4e0f0f17..0a99efd76d5a3 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs @@ -2,13 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; namespace Microsoft.Diagnostics.DataContractReader.Contracts; +public enum RejitState +{ + Requested, + Active +} + internal interface IReJIT : IContract { static string IContract.Name { get; } = nameof(ReJIT); + bool IsEnabled() => throw new NotImplementedException(); + + RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + + TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); } internal readonly struct ReJIT : IReJIT diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetNUInt.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetNUInt.cs index 8a565225ba831..3072c484edb2e 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetNUInt.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/TargetNUInt.cs @@ -7,10 +7,20 @@ namespace Microsoft.Diagnostics.DataContractReader; [DebuggerDisplay("{Hex}")] -public readonly struct TargetNUInt +public readonly struct TargetNUInt : IEquatable { public readonly ulong Value; public TargetNUInt(ulong value) => Value = value; internal string Hex => $"0x{Value:x}"; + + public override bool Equals(object? obj) => obj is TargetNUInt other && Equals(other); + + public bool Equals(TargetNUInt t) => Value == t.Value; + + public override int GetHashCode() => Value.GetHashCode(); + + public static bool operator ==(TargetNUInt lhs, TargetNUInt rhs) => lhs.Equals(rhs); + + public static bool operator !=(TargetNUInt lhs, TargetNUInt rhs) => !(lhs == rhs); } 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 37086698d95e0..bcf31efb72321 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 @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Diagnostics; +using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -16,6 +18,84 @@ public CodeVersions_1(Target target) _target = target; } + ILCodeVersionHandle ICodeVersions.GetActiveILCodeVersion(TargetPointer methodDesc) + { + // CodeVersionManager::GetActiveILCodeVersion + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer module = rts.GetModule(typeHandle); + uint methodDefToken = rts.GetMethodToken(md); + return FindActiveILCodeVersion(module, methodDefToken); + } + + ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativeCodeVersionHandle) + { + // NativeCodeVersion::GetILCodeVersion + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + if (!nativeCodeVersionHandle.Valid) + { + return ILCodeVersionHandle.Invalid; + } + + if (!IsExplicit(nativeCodeVersionHandle)) + { + // synthetic case + MethodDescHandle md = rts.GetMethodDescHandle(nativeCodeVersionHandle.MethodDescAddress); + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer module = rts.GetModule(typeHandle); + uint methodDefToken = rts.GetMethodToken(md); + return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + } + else + { + // explicit case + NativeCodeVersionNode nativeCodeVersionNode = AsNode(nativeCodeVersionHandle); + foreach (ILCodeVersionHandle ilCodeVersionHandle in ((ICodeVersions)this).GetILCodeVersions(nativeCodeVersionNode.MethodDesc)) + { + if (GetId(ilCodeVersionHandle) == nativeCodeVersionNode.ILVersionId) + { + return ilCodeVersionHandle; + } + } + } + + return ILCodeVersionHandle.Invalid; + } + + IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer methodDesc) + { + // CodeVersionManager::GetILCodeVersions + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + TargetPointer module = rts.GetModule(typeHandle); + uint methodDefToken = rts.GetMethodToken(md); + + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); + TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; + TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); + + // always add the synthetic version + yield return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + + // if explicit versions exist, iterate linked list and return them + if (ilVersionStateAddress != TargetPointer.Null) + { + Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); + TargetPointer nodePointer = ilState.FirstVersionNode; + while (nodePointer != TargetPointer.Null) + { + Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd(nodePointer); + yield return new ILCodeVersionHandle(TargetPointer.Null, 0, nodePointer); + nodePointer = current.Next; + } + } + } + NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointer ip) { // ExecutionManager::GetNativeCodeVersion(PCODE ip)) @@ -44,23 +124,6 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe } } - NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersion(TargetPointer methodDesc) - { - // CodeVersionManager::GetActiveILCodeVersion - // then ILCodeVersion::GetActiveNativeCodeVersion - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); - ILCodeVersionHandle methodDefActiveVersion = FindActiveILCodeVersion(module, methodDefToken); - if (!methodDefActiveVersion.IsValid) - { - return NativeCodeVersionHandle.Invalid; - } - return FindActiveNativeCodeVersion(methodDefActiveVersion, methodDesc); - } bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddress) { IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; @@ -98,27 +161,14 @@ TargetCodePointer ICodeVersions.GetNativeCode(NativeCodeVersionHandle codeVersio } } - internal struct ILCodeVersionHandle + NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle) { - internal readonly TargetPointer Module; - internal uint MethodDefinition; - internal readonly TargetPointer ILCodeVersionNode; - internal readonly uint RejitId; - - internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) + // ILCodeVersion::GetActiveNativeCodeVersion + if (!ilCodeVersionHandle.IsValid) { - if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null) - throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null"); - - if (module != TargetPointer.Null && methodDef == 0) - throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null"); - - Module = module; - MethodDefinition = methodDef; - ILCodeVersionNode = ilCodeVersionNodeAddress; + return NativeCodeVersionHandle.Invalid; } - public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); - public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; + return FindActiveNativeCodeVersion(ilCodeVersionHandle, methodDesc); } [Flags] @@ -173,7 +223,7 @@ private enum ILCodeVersionKind Explicit = 1, // means Node is set Synthetic = 2, // means Module and Token are set } - private static ILCodeVersionHandle ILCodeVersionHandleFromState(Data.ILCodeVersioningState ilState) + private static ILCodeVersionHandle ActiveILCodeVersionHandleFromState(Data.ILCodeVersioningState ilState) { switch ((ILCodeVersionKind)ilState.ActiveVersionKind) { @@ -198,7 +248,7 @@ private ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint m return new ILCodeVersionHandle(module, methodDefinition, TargetPointer.Null); } Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); - return ILCodeVersionHandleFromState(ilState); + return ActiveILCodeVersionHandleFromState(ilState); } [Flags] @@ -235,10 +285,10 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion } } - private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress) + private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle ilcodeVersion, TargetPointer methodDescAddress) { - TargetNUInt? ilVersionId = default; - if (methodDefActiveVersion.Module != TargetPointer.Null) + TargetNUInt ilVersionId = GetId(ilcodeVersion); + if (!IsExplicit(ilcodeVersion)) { NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); if (IsActiveNativeCodeVersion(provisionalHandle)) @@ -249,8 +299,8 @@ private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle else { // Get the explicit IL code version - Debug.Assert(methodDefActiveVersion.ILCodeVersionNode != TargetPointer.Null); - Data.ILCodeVersionNode ilCodeVersion = _target.ProcessedData.GetOrAdd(methodDefActiveVersion.ILCodeVersionNode); + Debug.Assert(ilcodeVersion.ILCodeVersionNode != TargetPointer.Null); + Data.ILCodeVersionNode ilCodeVersion = _target.ProcessedData.GetOrAdd(ilcodeVersion.ILCodeVersionNode); ilVersionId = ilCodeVersion.VersionId; } @@ -259,9 +309,49 @@ private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); return FindFirstCodeVersion(rts, md, (codeVersion) => { - return (!ilVersionId.HasValue || ilVersionId.Value.Value == codeVersion.ILVersionId.Value) + return (ilVersionId == codeVersion.ILVersionId) && ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); }); } + private static bool IsExplicit(ILCodeVersionHandle handle) + { + return handle.ILCodeVersionNode != TargetPointer.Null; + } + + private static bool IsExplicit(NativeCodeVersionHandle handle) + { + return handle.CodeVersionNodeAddress != TargetPointer.Null; + } + + private ILCodeVersionNode AsNode(ILCodeVersionHandle handle) + { + if (handle.ILCodeVersionNode == TargetPointer.Null) + { + throw new NotImplementedException("Synthetic ILCodeVersion does not have a backing node."); + } + + return _target.ProcessedData.GetOrAdd(handle.ILCodeVersionNode); + } + + private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle) + { + if (handle.CodeVersionNodeAddress == TargetPointer.Null) + { + throw new NotImplementedException("Synthetic NativeCodeVersion does not have a backing node."); + } + + return _target.ProcessedData.GetOrAdd(handle.CodeVersionNodeAddress); + } + + private TargetNUInt GetId(ILCodeVersionHandle ilCodeVersionHandle) + { + if (!IsExplicit(ilCodeVersionHandle)) + { + // for non explicit ILCodeVersions, id is always 0 + return new TargetNUInt(0); + } + ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle); + return ilCodeVersionNode.VersionId; + } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs index d5bcd98bd4b43..eee7d015f3a7b 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -17,6 +19,21 @@ private enum COR_PRF_MONITOR COR_PRF_ENABLE_REJIT = 0x00040000, } + // see src/coreclr/vm/codeversion.h + [Flags] + public enum RejitFlags : uint + { + kStateRequested = 0x00000000, + + kStateGettingReJITParameters = 0x00000001, + + kStateActive = 0x00000002, + + kStateMask = 0x0000000F, + + kSuppressParams = 0x80000000 + } + public ReJIT_1(Target target, Data.ProfControlBlock profControlBlock) { _target = target; @@ -32,4 +49,41 @@ bool IReJIT.IsEnabled() bool clrConfigEnabledReJIT = true; return profEnabledReJIT || clrConfigEnabledReJIT; } + + RejitState IReJIT.GetRejitState(ILCodeVersionHandle ilCodeVersionHandle) + { + if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) + { + // for non explicit ILCodeVersions, ReJITState is always kStateActive + return RejitState.Active; + } + ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle); + return ((RejitFlags)ilCodeVersionNode.RejitState & RejitFlags.kStateMask) switch + { + RejitFlags.kStateRequested => RejitState.Requested, + RejitFlags.kStateActive => RejitState.Active, + _ => throw new NotImplementedException($"Unknown ReJIT state: {ilCodeVersionNode.RejitState}"), + }; + } + + TargetNUInt IReJIT.GetRejitId(ILCodeVersionHandle ilCodeVersionHandle) + { + if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) + { + // for non explicit ILCodeVersions, ReJITId is always 0 + return new TargetNUInt(0); + } + ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle); + return ilCodeVersionNode.VersionId; + } + + private ILCodeVersionNode AsNode(ILCodeVersionHandle ilCodeVersionHandle) + { + if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) + { + throw new NotImplementedException("Synthetic ILCodeVersion does not have a backing node."); + } + + return _target.ProcessedData.GetOrAdd(ilCodeVersionHandle.ILCodeVersionNode); + } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersionNode.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersionNode.cs index fbc7183c6688e..fb6fdc32164b4 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersionNode.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersionNode.cs @@ -13,7 +13,13 @@ public ILCodeVersionNode(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.ILCodeVersionNode); VersionId = target.ReadNUInt(address + (ulong)type.Fields[nameof(VersionId)].Offset); + Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset); + RejitState = target.Read(address + (ulong)type.Fields[nameof(RejitState)].Offset); } public TargetNUInt VersionId { get; init; } + + public TargetPointer Next { get; init; } + + public uint RejitState { get; init; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersioningState.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersioningState.cs index 92a214d910f6d..a37e93ed9637e 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersioningState.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ILCodeVersioningState.cs @@ -12,12 +12,14 @@ public ILCodeVersioningState(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.ILCodeVersioningState); + FirstVersionNode = target.ReadPointer(address + (ulong)type.Fields[nameof(FirstVersionNode)].Offset); ActiveVersionKind = target.Read(address + (ulong)type.Fields[nameof(ActiveVersionKind)].Offset); ActiveVersionNode = target.ReadPointer(address + (ulong)type.Fields[nameof(ActiveVersionNode)].Offset); ActiveVersionModule = target.ReadPointer(address + (ulong)type.Fields[nameof(ActiveVersionModule)].Offset); ActiveVersionMethodDef = target.Read(address + (ulong)type.Fields[nameof(ActiveVersionMethodDef)].Offset); } + public TargetPointer FirstVersionNode { get; set; } public uint ActiveVersionKind { get; set; } public TargetPointer ActiveVersionNode { get; set; } public TargetPointer ActiveVersionModule { get; set; } diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index dce6b1bf9d0b2..5aabd599ee55c 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using System.Text; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -194,13 +197,10 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes int hr = HResults.E_NOTIMPL; try { - if (cRevertedRejitVersions != 0) - { - throw new NotImplementedException(); // TODO[cdac]: rejit - } Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc); Contracts.ICodeVersions nativeCodeContract = _target.Contracts.CodeVersions; + Contracts.IReJIT rejitContract = _target.Contracts.ReJIT; if (rgRevertedRejitData != null) { @@ -256,11 +256,100 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTableAddr); data->ModulePtr = rtsContract.GetModule(typeHandle); - // TODO[cdac]: everything in the ReJIT TRY/CATCH in GetMethodDescDataImpl in request.cpp - if (pcNeededRevertedRejitData != null) + // If rejit info is appropriate, get the following: + // * ReJitInfo for the current, active version of the method + // * ReJitInfo for the requested IP (for !ip2md and !u) + // * ReJitInfos for all reverted versions of the method (up to + // cRevertedRejitVersions) + // + // Minidumps will not have all this rejit info, and failure to get rejit info + // should not be fatal. So enclose all rejit stuff in a try. + try { + if (activeNativeCodeVersion is null || !activeNativeCodeVersion.Value.Valid) + { + activeNativeCodeVersion = nativeCodeContract.GetActiveNativeCodeVersion(new TargetPointer(methodDesc)); + } + + if (activeNativeCodeVersion is null || !activeNativeCodeVersion.Value.Valid) + { + throw new InvalidOperationException("No active native code version found"); + } + + // Active ReJitInfo + CopyNativeCodeVersionToReJitData( + activeNativeCodeVersion.Value, + activeNativeCodeVersion.Value, + &data->rejitDataCurrent); + + // Requested ReJitInfo + Debug.Assert(data->rejitDataRequested.rejitID == 0); + if (ip != 0 && requestedNativeCodeVersion.Valid) + { + CopyNativeCodeVersionToReJitData( + requestedNativeCodeVersion, + activeNativeCodeVersion.Value, + &data->rejitDataRequested); + } - throw new NotImplementedException(); // TODO[cdac]: rejit stuff + // Total number of jitted rejit versions + int cJittedRejitVersions = rejitContract.GetRejitIds(_target, methodDescHandle.Address).Count(); + data->cJittedRejitVersions = (uint)cJittedRejitVersions; + + // Reverted ReJitInfos + if (rgRevertedRejitData == null) + { + // No reverted rejit versions will be returned, but maybe caller wants a + // count of all versions + if (pcNeededRevertedRejitData != null) + { + *pcNeededRevertedRejitData = data->cJittedRejitVersions; + } + } + else + { + // Caller wants some reverted rejit versions. Gather reverted rejit version data to return + + // Prepare array to populate with rejitids. "+ 1" because GetReJITIDs + // returns all available rejitids, including the rejitid for the one non-reverted + // current version. + List reJitIds = rejitContract.GetRejitIds(_target, methodDescHandle.Address).ToList(); + + // Go through rejitids. For each reverted one, populate a entry in rgRevertedRejitData + uint iRejitDataReverted = 0; + ILCodeVersionHandle activeVersion = nativeCodeContract.GetActiveILCodeVersion(methodDesc); + TargetNUInt activeVersionId = rejitContract.GetRejitId(activeVersion); + for (uint i = 0; (i < reJitIds.Count) && (iRejitDataReverted < cRevertedRejitVersions); i++) + { + ILCodeVersionHandle ilCodeVersion = nativeCodeContract.GetILCodeVersions(methodDesc) + .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode) == reJitIds[(int)i], + ILCodeVersionHandle.Invalid); + + + if (!ilCodeVersion.IsValid || rejitContract.GetRejitId(ilCodeVersion) == activeVersionId) + { + continue; + } + + NativeCodeVersionHandle activeRejitChild = nativeCodeContract.GetActiveNativeCodeVersionForILCodeVersion(methodDesc, ilCodeVersion); + CopyNativeCodeVersionToReJitData( + activeRejitChild, + activeNativeCodeVersion.Value, + &rgRevertedRejitData[iRejitDataReverted]); + + iRejitDataReverted++; + } + // pcNeededRevertedRejitData != NULL as per condition at top of function (cuz rgRevertedRejitData != + // NULL). + *pcNeededRevertedRejitData = iRejitDataReverted; + } + } + catch (global::System.Exception) + { + if (pcNeededRevertedRejitData != null) + { + *pcNeededRevertedRejitData = 0; + } } #if false // TODO[cdac]: HAVE_GCCOVER @@ -297,7 +386,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes rgRevertedRejitDataLocal = new DacpReJitData[cRevertedRejitVersions]; } uint cNeededRevertedRejitDataLocal = 0; - uint *pcNeededRevertedRejitDataLocal = null; + uint* pcNeededRevertedRejitDataLocal = null; if (pcNeededRevertedRejitData != null) { pcNeededRevertedRejitDataLocal = &cNeededRevertedRejitDataLocal; @@ -321,12 +410,22 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes Debug.Assert(data->GCStressCodeCopy == dataLocal.GCStressCodeCopy); Debug.Assert(data->managedDynamicMethodObject == dataLocal.managedDynamicMethodObject); Debug.Assert(data->requestedIP == dataLocal.requestedIP); - // TODO[cdac]: cdacreader always returns 0 currently - Debug.Assert(data->cJittedRejitVersions == 0 || data->cJittedRejitVersions == dataLocal.cJittedRejitVersions); - // TODO[cdac]: compare rejitDataCurrent and rejitDataRequested, too + Debug.Assert(data->cJittedRejitVersions == dataLocal.cJittedRejitVersions); + + // rejitDataCurrent + Debug.Assert(data->rejitDataCurrent.rejitID == dataLocal.rejitDataCurrent.rejitID); + Debug.Assert(data->rejitDataCurrent.NativeCodeAddr == dataLocal.rejitDataCurrent.NativeCodeAddr); + Debug.Assert(data->rejitDataCurrent.flags == dataLocal.rejitDataCurrent.flags); + + // rejitDataRequested + Debug.Assert(data->rejitDataRequested.rejitID == dataLocal.rejitDataRequested.rejitID); + Debug.Assert(data->rejitDataRequested.NativeCodeAddr == dataLocal.rejitDataRequested.NativeCodeAddr); + Debug.Assert(data->rejitDataRequested.flags == dataLocal.rejitDataRequested.flags); + + // rgRevertedRejitData if (rgRevertedRejitData != null && rgRevertedRejitDataLocal != null) { - Debug.Assert (cNeededRevertedRejitDataLocal == *pcNeededRevertedRejitData); + Debug.Assert(cNeededRevertedRejitDataLocal == *pcNeededRevertedRejitData); for (ulong i = 0; i < cNeededRevertedRejitDataLocal; i++) { Debug.Assert(rgRevertedRejitData[i].rejitID == rgRevertedRejitDataLocal[i].rejitID); @@ -343,6 +442,45 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes return hr; } + private void CopyNativeCodeVersionToReJitData( + NativeCodeVersionHandle nativeCodeVersion, + NativeCodeVersionHandle activeNativeCodeVersion, + DacpReJitData* pReJitData) + { + ICodeVersions cv = _target.Contracts.CodeVersions; + IReJIT rejit = _target.Contracts.ReJIT; + + ILCodeVersionHandle ilCodeVersion = cv.GetILCodeVersion(nativeCodeVersion); + + pReJitData->rejitID = rejit.GetRejitId(ilCodeVersion).Value; + pReJitData->NativeCodeAddr = cv.GetNativeCode(nativeCodeVersion); + + if (nativeCodeVersion.CodeVersionNodeAddress != activeNativeCodeVersion.CodeVersionNodeAddress || + nativeCodeVersion.MethodDescAddress != activeNativeCodeVersion.MethodDescAddress) + { + pReJitData->flags = DacpReJitData.Flags.kReverted; + } + else + { + DacpReJitData.Flags flags = DacpReJitData.Flags.kUnknown; + switch (rejit.GetRejitState(ilCodeVersion)) + { + // kStateRequested + case RejitState.Requested: + flags = DacpReJitData.Flags.kRequested; + break; + // kStateActive + case RejitState.Active: + flags = DacpReJitData.Flags.kActive; + break; + default: + Debug.Fail("Unknown SharedRejitInfo state. cDAC should be updated to understand this new state."); + break; + } + pReJitData->flags = flags; + } + } + int ISOSDacInterface.GetMethodDescFromToken(ulong moduleAddr, uint token, ulong* methodDesc) => _legacyImpl is not null ? _legacyImpl.GetMethodDescFromToken(moduleAddr, token, methodDesc) : HResults.E_NOTIMPL; int ISOSDacInterface.GetMethodDescName(ulong methodDesc, uint count, char* name, uint* pNeeded) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index f60f75df5947a..fcaac0d46c264 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; using Moq; using Xunit; @@ -345,7 +347,12 @@ public void GetActiveNativeCodeVersion_DefaultCase(MockTarget.Architecture arch) var methodDescAddress = new TargetPointer(0x00aa_aa00); var moduleAddress = new TargetPointer(0x00ca_ca00); - TargetPointer versioningState = builder.AddILCodeVersioningState(activeVersionKind: 0/*==unknown*/, activeVersionNode: TargetPointer.Null, activeVersionModule: moduleAddress, activeVersionMethodDef: methodDefToken); + TargetPointer versioningState = builder.AddILCodeVersioningState( + activeVersionKind: 0/*==unknown*/, + activeVersionNode: TargetPointer.Null, + activeVersionModule: moduleAddress, + activeVersionMethodDef: methodDefToken, + firstVersionNode: TargetPointer.Null); var oneModule = new MockModule() { Address = moduleAddress, MethodDefToILCodeVersioningStateAddress = new TargetPointer(0x00da_da00), @@ -399,7 +406,12 @@ private void GetActiveNativeCodeVersion_IterateVersionNodes_Impl(MockTarget.Arch var methodDescAddress = new TargetPointer(0x00aa_aa00); var moduleAddress = new TargetPointer(0x00ca_ca00); - TargetPointer versioningState = builder.AddILCodeVersioningState(activeVersionKind: 0/*==unknown*/, activeVersionNode: TargetPointer.Null, activeVersionModule: moduleAddress, activeVersionMethodDef: methodDefToken); + TargetPointer versioningState = builder.AddILCodeVersioningState( + activeVersionKind: 0/*==unknown*/, + activeVersionNode: TargetPointer.Null, + activeVersionModule: moduleAddress, + activeVersionMethodDef: methodDefToken, + firstVersionNode: TargetPointer.Null); var module = new MockModule() { Address = moduleAddress, @@ -468,8 +480,13 @@ private void GetActiveNativeCodeVersion_ExplicitILCodeVersion_Impl(MockTarget.Ar var moduleAddress = new TargetPointer(0x00ca_ca00); TargetNUInt ilVersionId = new TargetNUInt(5); - TargetPointer ilVersionNode = builder.AddILCodeVersionNode(ilVersionId); - TargetPointer versioningState = builder.AddILCodeVersioningState(activeVersionKind: 1 /* Explicit */, activeVersionNode: ilVersionNode, activeVersionModule: TargetPointer.Null, activeVersionMethodDef: 0); + TargetPointer ilVersionNode = builder.AddILCodeVersionNode(TargetPointer.Null, ilVersionId, /* kStateActive */ 0x00000002); + TargetPointer versioningState = builder.AddILCodeVersioningState( + activeVersionKind: 1 /* Explicit */, + activeVersionNode: ilVersionNode, + activeVersionModule: TargetPointer.Null, + activeVersionMethodDef: 0, + firstVersionNode: ilVersionNode); var module = new MockModule() { Address = moduleAddress, @@ -515,4 +532,137 @@ private void GetActiveNativeCodeVersion_ExplicitILCodeVersion_Impl(MockTarget.Ar Assert.False(handle.Valid); } } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetILCodeVersions_SyntheticAndExplicit(MockTarget.Architecture arch) + { + uint methodRowId = 0x25; // arbitrary + TargetCodePointer expectedSyntheticCodePointer = new TargetCodePointer(0x0700_abc0); + TargetCodePointer expectedExplicitCodePointer = new TargetCodePointer(0x0780_abc0); + var builder = new MockCodeVersions(arch); + var methodDescAddress = new TargetPointer(0x00aa_aa00); + var moduleAddress = new TargetPointer(0x00ca_ca00); + + TargetNUInt ilVersionId = new TargetNUInt(2); + TargetPointer ilVersionNode = builder.AddILCodeVersionNode(TargetPointer.Null, ilVersionId, /* kStateActive */ 0x00000002); + TargetPointer versioningState = builder.AddILCodeVersioningState( + activeVersionKind: 1 /* Explicit */, + activeVersionNode: ilVersionNode, + activeVersionModule: TargetPointer.Null, + activeVersionMethodDef: 0, + firstVersionNode: ilVersionNode); + var module = new MockModule() + { + Address = moduleAddress, + MethodDefToILCodeVersioningStateAddress = new TargetPointer(0x00da_da00), + MethodDefToILCodeVersioningStateTable = new Dictionary() { + { methodRowId, versioningState} + }, + }; + var methodTable = new MockMethodTable() + { + Address = new TargetPointer(0x00ba_ba00), + Module = module, + }; + + (TargetPointer firstNode, TargetPointer activeSyntheticNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); + (firstNode, TargetPointer activeExplicitNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); + + TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); + + var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress, nativeCode: expectedExplicitCodePointer); + oneMethod.MethodTable = methodTable; + oneMethod.RowId = methodRowId; + + var target = CreateTarget(arch, [oneMethod], [methodTable], [], [module], builder); + + // TEST + + var codeVersions = target.Contracts.CodeVersions; + Assert.NotNull(codeVersions); + + // Get all ILCodeVersions + List ilCodeVersions = codeVersions.GetILCodeVersions(methodDescAddress).ToList(); + Assert.Equal(2, ilCodeVersions.Count); + + // Get the explicit ILCodeVersion and assert that it is in the list of ILCodeVersions + ILCodeVersionHandle explicitILcodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); + Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILcodeVersion)); + Assert.Equal(expectedExplicitCodePointer, codeVersions.GetNativeCode(codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILcodeVersion))); + + // Find the other ILCodeVersion (synthetic) and assert that it is valid. + ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILcodeVersion)); + Assert.True(syntheticILcodeVersion.IsValid); + Assert.Equal(expectedSyntheticCodePointer, codeVersions.GetNativeCode(codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion))); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetILCodeVersionFromNativeCodeVersion_SyntheticAndExplicit(MockTarget.Architecture arch) + { + uint methodRowId = 0x25; // arbitrary + TargetCodePointer expectedSyntheticCodePointer = new TargetCodePointer(0x0700_abc0); + TargetCodePointer expectedExplicitCodePointer = new TargetCodePointer(0x0780_abc0); + var builder = new MockCodeVersions(arch); + var methodDescAddress = new TargetPointer(0x00aa_aa00); + var moduleAddress = new TargetPointer(0x00ca_ca00); + + TargetNUInt ilVersionId = new TargetNUInt(2); + TargetPointer ilVersionNode = builder.AddILCodeVersionNode(TargetPointer.Null, ilVersionId, /* kStateActive */ 0x00000002); + TargetPointer versioningState = builder.AddILCodeVersioningState( + activeVersionKind: 1 /* Explicit */, + activeVersionNode: ilVersionNode, + activeVersionModule: TargetPointer.Null, + activeVersionMethodDef: 0, + firstVersionNode: ilVersionNode); + var module = new MockModule() + { + Address = moduleAddress, + MethodDefToILCodeVersioningStateAddress = new TargetPointer(0x00da_da00), + MethodDefToILCodeVersioningStateTable = new Dictionary() { + { methodRowId, versioningState} + }, + }; + var methodTable = new MockMethodTable() + { + Address = new TargetPointer(0x00ba_ba00), + Module = module, + }; + + (TargetPointer firstNode, TargetPointer activeSyntheticNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); + (firstNode, TargetPointer activeExplicitNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); + + TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); + + var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress, nativeCode: expectedExplicitCodePointer); + oneMethod.MethodTable = methodTable; + oneMethod.RowId = methodRowId; + + var target = CreateTarget(arch, [oneMethod], [methodTable], [], [module], builder); + + // TEST + + var codeVersions = target.Contracts.CodeVersions; + Assert.NotNull(codeVersions); + + // Get all ILCodeVersions + List ilCodeVersions = codeVersions.GetILCodeVersions(methodDescAddress).ToList(); + Assert.Equal(2, ilCodeVersions.Count); + + // Get the explicit ILCodeVersion and assert that it is in the list of ILCodeVersions + ILCodeVersionHandle explicitILcodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); + Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILcodeVersion)); + + // Find the other ILCodeVersion (synthetic) and assert that it is valid. + ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILcodeVersion)); + Assert.True(syntheticILcodeVersion.IsValid); + + // Verify getting ILCode is equal to ILCode from NativeCode from ILCode. + NativeCodeVersionHandle explicitNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILcodeVersion); + Assert.True(explicitILcodeVersion.Equals(codeVersions.GetILCodeVersion(explicitNativeCodeVersion))); + + NativeCodeVersionHandle syntheticNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion); + Assert.True(syntheticILcodeVersion.Equals(codeVersions.GetILCodeVersion(syntheticNativeCodeVersion))); + } } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index f97f099433e9e..1931c9c367917 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -42,6 +42,7 @@ public class CodeVersions DataType = DataType.ILCodeVersioningState, Fields = [ + new(nameof(Data.ILCodeVersioningState.FirstVersionNode), DataType.pointer), new(nameof(Data.ILCodeVersioningState.ActiveVersionMethodDef), DataType.uint32), new(nameof(Data.ILCodeVersioningState.ActiveVersionModule), DataType.pointer), new(nameof(Data.ILCodeVersioningState.ActiveVersionKind), DataType.uint32), @@ -55,6 +56,8 @@ public class CodeVersions Fields = [ new(nameof(Data.ILCodeVersionNode.VersionId), DataType.nuint), + new(nameof(Data.ILCodeVersionNode.Next), DataType.pointer), + new(nameof(Data.ILCodeVersionNode.RejitState), DataType.uint32), ] }; @@ -67,6 +70,10 @@ public CodeVersions(MockTarget.Architecture arch) : this(new MockMemorySpace.Builder(new TargetTestHelpers(arch)), (DefaultAllocationRangeStart, DefaultAllocationRangeEnd)) { } + public CodeVersions(MockMemorySpace.Builder builder) + : this(builder, (DefaultAllocationRangeStart, DefaultAllocationRangeEnd)) + { } + public CodeVersions(MockTarget.Architecture arch, (ulong Start, ulong End) allocationRange) : this(new MockMemorySpace.Builder(new TargetTestHelpers(arch)), allocationRange) { } @@ -120,16 +127,16 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe Builder.TargetTestHelpers.WriteNUInt(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.ILVersionId)].Offset, Builder.TargetTestHelpers.PointerSize), ilVersionId); } - public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt explicitILVersion) + public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt explicitILVersion, TargetPointer? firstNode = null) { TargetPointer activeVersionNode = TargetPointer.Null; - TargetPointer next = TargetPointer.Null; + TargetPointer next = firstNode != null ? firstNode.Value : TargetPointer.Null; for (int i = count - 1; i >= 0; i--) { TargetPointer node = AddNativeCodeVersionNode(); bool isActive = i == activeIndex; TargetCodePointer nativeCode = isActive ? activeNativeCode : 0; - TargetNUInt ilVersionId = isActive ? explicitILVersion : default; + TargetNUInt ilVersionId = explicitILVersion; FillNativeCodeVersionNode(node, methodDesc, nativeCode, next, isActive, ilVersionId); next = node; if (isActive) @@ -139,7 +146,7 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe return (next, activeVersionNode); } - public TargetPointer AddILCodeVersioningState(uint activeVersionKind, TargetPointer activeVersionNode, TargetPointer activeVersionModule, uint activeVersionMethodDef) + public TargetPointer AddILCodeVersioningState(uint activeVersionKind, TargetPointer activeVersionNode, TargetPointer activeVersionModule, uint activeVersionMethodDef, TargetPointer firstVersionNode) { Target.TypeInfo info = Types[DataType.ILCodeVersioningState]; MockMemorySpace.HeapFragment fragment = _codeVersionsAllocator.Allocate((ulong)Types[DataType.ILCodeVersioningState].Size, "ILCodeVersioningState"); @@ -149,15 +156,28 @@ public TargetPointer AddILCodeVersioningState(uint activeVersionKind, TargetPoin Builder.TargetTestHelpers.WritePointer(ilcvs.Slice(info.Fields[nameof(Data.ILCodeVersioningState.ActiveVersionNode)].Offset, Builder.TargetTestHelpers.PointerSize), activeVersionNode); Builder.TargetTestHelpers.Write(ilcvs.Slice(info.Fields[nameof(Data.ILCodeVersioningState.ActiveVersionMethodDef)].Offset, sizeof(uint)), activeVersionMethodDef); Builder.TargetTestHelpers.Write(ilcvs.Slice(info.Fields[nameof(Data.ILCodeVersioningState.ActiveVersionKind)].Offset, sizeof(uint)), activeVersionKind); + Builder.TargetTestHelpers.WritePointer(ilcvs.Slice(info.Fields[nameof(Data.ILCodeVersioningState.FirstVersionNode)].Offset), firstVersionNode); return fragment.Address; } - public TargetPointer AddILCodeVersionNode(TargetNUInt versionId) + public TargetPointer AddILCodeVersionNode(TargetPointer prevNodeAddress, TargetNUInt versionId, uint rejitFlags) { Target.TypeInfo info = Types[DataType.ILCodeVersionNode]; MockMemorySpace.HeapFragment fragment = _codeVersionsAllocator.Allocate((ulong)Types[DataType.ILCodeVersionNode].Size, "NativeCodeVersionNode"); Builder.AddHeapFragment(fragment); Builder.TargetTestHelpers.WriteNUInt(fragment.Data.AsSpan().Slice(info.Fields[nameof(Data.ILCodeVersionNode.VersionId)].Offset), versionId); + Builder.TargetTestHelpers.Write(fragment.Data.AsSpan().Slice(info.Fields[nameof(Data.ILCodeVersionNode.RejitState)].Offset), (uint)rejitFlags); + + // set new node next pointer to null + Builder.TargetTestHelpers.WritePointer(fragment.Data.AsSpan().Slice(info.Fields[nameof(Data.ILCodeVersionNode.Next)].Offset), TargetPointer.Null); + + // set the previous node next pointer to the new node + if(prevNodeAddress != TargetPointer.Null) + { + Span prevNode = Builder.BorrowAddressRange(prevNodeAddress, fragment.Data.Length); + Builder.TargetTestHelpers.WritePointer(prevNode.Slice(info.Fields[nameof(Data.ILCodeVersionNode.Next)].Offset), fragment.Address); + } + return fragment.Address; } } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs new file mode 100644 index 0000000000000..3cf509a273173 --- /dev/null +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs @@ -0,0 +1,101 @@ +// 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 ReJIT + { + private const ulong DefaultAllocationRangeStart = 0x0010_1000; + private const ulong DefaultAllocationRangeEnd = 0x00011_0000; + + // see src/coreclr/vm/codeversion.h + [Flags] + public enum RejitFlags : uint + { + kStateRequested = 0x00000000, + + kStateGettingReJITParameters = 0x00000001, + + kStateActive = 0x00000002, + + kStateMask = 0x0000000F, + + kSuppressParams = 0x80000000 + } + + private static readonly TypeFields ProfControlBlockFields = new TypeFields() + { + DataType = DataType.ProfControlBlock, + Fields = + [ + new(nameof(Data.ProfControlBlock.GlobalEventMask), DataType.uint64) + ] + }; + + internal readonly MockMemorySpace.Builder Builder; + + internal Dictionary Types { get; } + internal (string Name, ulong Value)[] Globals { get; } + + private CodeVersions _codeVersions { get; } + + private readonly MockMemorySpace.BumpAllocator _rejitAllocator; + + public ReJIT(MockTarget.Architecture arch) + : this(new MockMemorySpace.Builder(new TargetTestHelpers(arch)), (DefaultAllocationRangeStart, DefaultAllocationRangeEnd)) + { } + + public ReJIT(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocationRange) + { + Builder = builder; + _rejitAllocator = Builder.CreateAllocator(allocationRange.Start, allocationRange.End); + + _codeVersions = new CodeVersions(Builder); + + Types = GetTypes(builder.TargetTestHelpers); + + Globals = + [ + (nameof(Constants.Globals.ProfilerControlBlock), AddProfControlBlock()), + ]; + } + + public ILCodeVersionHandle AddExplicitILCodeVersion(TargetNUInt rejitId, RejitFlags rejitFlags) + { + TargetPointer codeVersionNode = _codeVersions.AddILCodeVersionNode(TargetPointer.Null, rejitId, (uint)rejitFlags); + + return new ILCodeVersionHandle(TargetPointer.Null, 0, codeVersionNode); + } + + internal static Dictionary GetTypes(TargetTestHelpers helpers) + { + Dictionary cvTypes = CodeVersions.GetTypes(helpers); + Dictionary types = GetTypesForTypeFields( + helpers, + [ + ProfControlBlockFields + ]); + foreach(var (dataType, typeInfo) in cvTypes) + { + types.Add(dataType, typeInfo); + } + return types; + } + + private ulong AddProfControlBlock() + { + Target.TypeInfo info = Types[DataType.ProfControlBlock]; + MockMemorySpace.HeapFragment fragment = _rejitAllocator.Allocate((ulong)Types[DataType.ProfControlBlock].Size, "ProfControlBlock"); + Builder.AddHeapFragment(fragment); + Span pcb = Builder.BorrowAddressRange(fragment.Address, fragment.Data.Length); + Builder.TargetTestHelpers.Write(pcb.Slice(info.Fields[nameof(Data.ProfControlBlock.GlobalEventMask)].Offset, sizeof(ulong)), 0ul); + return fragment.Address; + } + } +} diff --git a/src/native/managed/cdacreader/tests/ReJITTests.cs b/src/native/managed/cdacreader/tests/ReJITTests.cs new file mode 100644 index 0000000000000..b6575a875ca65 --- /dev/null +++ b/src/native/managed/cdacreader/tests/ReJITTests.cs @@ -0,0 +1,131 @@ +// 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 Moq; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +using MockReJIT = MockDescriptors.ReJIT; + +public class ReJITTests +{ + internal static Target CreateTarget( + MockTarget.Architecture arch, + MockReJIT builder = null, + Mock mockCodeVersions = null) + { + builder ??= new MockReJIT(arch); + + TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types, builder.Globals); + + mockCodeVersions ??= new Mock(); + + IContractFactory rejitFactory = new ReJITFactory(); + + ContractRegistry reg = Mock.Of( + c => c.ReJIT == rejitFactory.CreateContract(target, 1) + && c.CodeVersions == mockCodeVersions.Object); + target.SetContracts(reg); + return target; + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRejitIdOk(MockTarget.Architecture arch) + { + MockReJIT mockRejit = new MockReJIT(arch); + + Dictionary expectedRejitIds = new() + { + // synthetic ILCodeVersionHandle + { new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), new TargetNUInt(0) }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), new TargetNUInt(1) }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(2) }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(3) } + }; + + var target = CreateTarget(arch, mockRejit); + + // TEST + + var rejit = target.Contracts.ReJIT; + Assert.NotNull(rejit); + + foreach (var (ilCodeVersionHandle, expectedRejitId) in expectedRejitIds) + { + TargetNUInt rejitState = rejit.GetRejitId(ilCodeVersionHandle); + Assert.Equal(expectedRejitId, rejitState); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRejitStateOk(MockTarget.Architecture arch) + { + MockReJIT mockRejit = new MockReJIT(arch); + + Dictionary expectedRejitStates = new() + { + // synthetic ILCodeVersionHandle + { new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), RejitState.Active }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), RejitState.Active }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), RejitState.Requested }, + { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kSuppressParams | MockReJIT.RejitFlags.kStateRequested), RejitState.Requested } + }; + + var target = CreateTarget(arch, mockRejit); + + // TEST + + var rejit = target.Contracts.ReJIT; + Assert.NotNull(rejit); + + foreach (var (ilCodeVersionHandle, expectedRejitState) in expectedRejitStates) + { + RejitState rejitState = rejit.GetRejitState(ilCodeVersionHandle); + Assert.Equal(expectedRejitState, rejitState); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetRejitIdsOk(MockTarget.Architecture arch) + { + MockReJIT mockRejit = new MockReJIT(arch); + Mock mockCodeVersions = new Mock(); + + List expectedRejitIds = [0, 1]; + expectedRejitIds.Sort(); + + List ilCodeVersionHandles = + [ + // synthetic ILCodeVersionHandle + new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), + mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), + mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested) + ]; + + TargetPointer methodDesc = new TargetPointer(/* arbitrary */ 0x200); + mockCodeVersions.Setup(cv => cv.GetILCodeVersions(methodDesc)) + .Returns(ilCodeVersionHandles); + var target = CreateTarget(arch, mockRejit, mockCodeVersions); + + // TEST + + var rejit = target.Contracts.ReJIT; + Assert.NotNull(rejit); + + List rejitIds = rejit.GetRejitIds(target, methodDesc) + .Select(e => e.Value) + .ToList(); + rejitIds.Sort(); + + Assert.Equal(expectedRejitIds, rejitIds); + } +} From 7ca17de83d82a062a5c988e291375ee6ee4a91c3 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Fri, 22 Nov 2024 15:17:48 -0500 Subject: [PATCH 02/11] fix case --- .../Contracts/Extensions/ICodeVersionsExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs index 157923d5d999e..37cd53791f576 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/Extensions/ICodeVersionsExtensions.cs @@ -7,7 +7,7 @@ internal static class ICodeVersionsExtensions { internal static NativeCodeVersionHandle GetActiveNativeCodeVersion(this ICodeVersions cv, TargetPointer methodDesc) { - ILCodeVersionHandle iLCodeVersionHandle = cv.GetActiveILCodeVersion(methodDesc); - return cv.GetActiveNativeCodeVersionForILCodeVersion(methodDesc, iLCodeVersionHandle); + ILCodeVersionHandle ilCodeVersionHandle = cv.GetActiveILCodeVersion(methodDesc); + return cv.GetActiveNativeCodeVersionForILCodeVersion(methodDesc, ilCodeVersionHandle); } } From cb22d7b0afcca575d8e2a668ffc3cfa1fc934f96 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Fri, 22 Nov 2024 15:32:53 -0500 Subject: [PATCH 03/11] improve comments and use helpers --- .../Contracts/CodeVersions_1.cs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 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 bcf31efb72321..5760592e4f7fa 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 @@ -41,7 +41,8 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ if (!IsExplicit(nativeCodeVersionHandle)) { - // synthetic case + // There is only a single synthetic NativeCodeVersion per + // method and it must be on the synthetic ILCodeVersion MethodDescHandle md = rts.GetMethodDescHandle(nativeCodeVersionHandle.MethodDescAddress); TargetPointer mtAddr = rts.GetMethodTable(md); TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); @@ -51,7 +52,7 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ } else { - // explicit case + // Otherwise filter all the ILCodeVersions for the one that matches the version id NativeCodeVersionNode nativeCodeVersionNode = AsNode(nativeCodeVersionHandle); foreach (ILCodeVersionHandle ilCodeVersionHandle in ((ICodeVersions)this).GetILCodeVersions(nativeCodeVersionNode.MethodDesc)) { @@ -145,20 +146,21 @@ bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddr TargetCodePointer ICodeVersions.GetNativeCode(NativeCodeVersionHandle codeVersionHandle) { - if (codeVersionHandle.MethodDescAddress != TargetPointer.Null) + if (!codeVersionHandle.Valid) + { + throw new ArgumentException("Invalid NativeCodeVersionHandle"); + } + + if (!IsExplicit(codeVersionHandle)) { MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); return _target.Contracts.RuntimeTypeSystem.GetNativeCode(md); } - else if (codeVersionHandle.CodeVersionNodeAddress != TargetPointer.Null) + else { Data.NativeCodeVersionNode nativeCodeVersionNode = _target.ProcessedData.GetOrAdd(codeVersionHandle.CodeVersionNodeAddress); return nativeCodeVersionNode.NativeCode; } - else - { - throw new ArgumentException("Invalid NativeCodeVersionHandle"); - } } NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle) @@ -260,7 +262,12 @@ internal enum NativeCodeVersionNodeFlags : uint private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion) { // NativeCodeVersion::IsActiveChildVersion - if (nativeCodeVersion.MethodDescAddress != TargetPointer.Null) + if (!nativeCodeVersion.Valid) + { + throw new ArgumentException("Invalid NativeCodeVersionHandle"); + } + + if (!IsExplicit(nativeCodeVersion)) { MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress); TargetPointer versioningStateAddress = _target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md); @@ -273,16 +280,12 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion MethodDescVersioningStateFlags flags = (MethodDescVersioningStateFlags)versioningState.Flags; return flags.HasFlag(MethodDescVersioningStateFlags.IsDefaultVersionActiveChildFlag); } - else if (nativeCodeVersion.CodeVersionNodeAddress != TargetPointer.Null) + else { // NativeCodeVersionNode::IsActiveChildVersion Data.NativeCodeVersionNode codeVersion = _target.ProcessedData.GetOrAdd(nativeCodeVersion.CodeVersionNodeAddress); return ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); } - else - { - throw new ArgumentException("Invalid NativeCodeVersionHandle"); - } } private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle ilcodeVersion, TargetPointer methodDescAddress) From 2fcd31c889afebf994e7e86316ed98f2eda0773f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 25 Nov 2024 12:11:12 -0500 Subject: [PATCH 04/11] comments --- docs/design/datacontracts/CodeVersions.md | 133 ++++-------------- .../Contracts/CodeVersions_1.cs | 104 ++++++-------- .../cdacreader/tests/CodeVersionsTests.cs | 7 +- .../MockDescriptors.CodeVersions.cs | 4 +- 4 files changed, 78 insertions(+), 170 deletions(-) diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 7bab465b0a761..c1dd2d70fb2f8 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -58,8 +58,6 @@ public virtual IEnumerable GetILCodeVersions(TargetPointer // Return a handle to the version of the native code that includes the given instruction pointer public virtual NativeCodeVersionHandle GetNativeCodeVersionForIP(TargetCodePointer ip); -// Return a handle to the active version of the native code for a given method descriptor -public virtual NativeCodeVersionHandle GetActiveNativeCodeVersion(TargetPointer methodDesc); // Return a handle to the active version of the native code for a given method descriptor and IL code version. The IL code version and method descriptor must represent the same method public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle); @@ -69,6 +67,11 @@ 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); ``` +### Extension Methods +```csharp +// Return a handle to the active version of the native code for a given method descriptor +public static NativeCodeVersionHandle GetActiveNativeCodeVersion(this ICodeVersions, TargetPointer methodDesc); +``` ## Version 1 @@ -127,17 +130,28 @@ Contracts used: | Loader | | RuntimeTypeSystem | +### Finding active ILCodeVersion for a method +```csharp +public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc); +``` +1. Check if the method has an `ILCodeVersioningState`. +2. If the method does not have an `ILCodeVersioningState`, the synthetic ILCodeVersion must be active. Return the synthetic ILCodeVersion for the method. +3. Otherwise, read the active ILCodeVersion off of the `ILCodeVersioningState`. + +### Finding ILCodeVersion from a NativeCodeVersion +```csharp +public virtual ILCodeVersionHandle GetILCodeVersion(NativeCodeVersionHandle nativeCodeVersionHandle); +``` +1. If `nativeCodeVersionHandle` is invalid, return an invalid `ILCodeVersionHandle`. +2. If `nativeCodeVersionHandle` is synthetic, the corresponding ILCodeVersion must also be synthetic; return the synthetic ILCodeVersion for the method. +3. Search the linked list of ILCodeVersions for one with the matching ILVersionId. Return the ILCodeVersion if found. Otherwise return invalid. + ### Finding all of the ILCodeVersions for a method ```csharp IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer methodDesc) { // CodeVersionManager::GetILCodeVersions - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); + GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken); ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; @@ -230,106 +244,15 @@ NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, MethodDescH } ``` -### Finding the active native code version of a method descriptor - +### Finding the active native code version of an ILCodeVersion for a method descriptor ```csharp -NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersion(TargetPointer methodDesc) -{ - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); - ILCodeVersionHandle methodDefActiveVersion = FindActiveILCodeVersion(module, methodDefToken); - if (!methodDefActiveVersion.IsValid) - { - return NativeCodeVersionHandle.Invalid; - } - return FindActiveNativeCodeVersion(methodDefActiveVersion, methodDesc); -} - -ILCodeVersionHandle ILCodeVersionHandleFromState(Data.ILCodeVersioningState ilState) -{ - switch ((ILCodeVersionKind)ilState.ActiveVersionKind) - { - case ILCodeVersionKind.Explicit: - return new ILCodeVersionHandle(module: TargetPointer.Null, methodDef: 0, ilState.ActiveVersionNode); - case ILCodeVersionKind.Synthetic: - case ILCodeVersionKind.Unknown: - return new ILCodeVersionHandle(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef, TargetPointer.Null); - default: - throw new InvalidOperationException($"Unknown ILCodeVersionKind {ilState.ActiveVersionKind}"); - } -} - -ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint methodDefinition) -{ - ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); - TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; - TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefinition, out var _); - if (ilVersionStateAddress == TargetPointer.Null) - { - return new ILCodeVersionHandle(module, methodDefinition, TargetPointer.Null); - } - Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); - return ILCodeVersionHandleFromState(ilState); -} - -bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion) -{ - if (nativeCodeVersion.MethodDescAddress != TargetPointer.Null) - { - MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress); - TargetPointer versioningStateAddress = _target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md); - if (versioningStateAddress == TargetPointer.Null) - { - return true; - } - Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd(versioningStateAddress); - MethodDescVersioningStateFlags flags = (MethodDescVersioningStateFlags)versioningState.Flags; - return flags.HasFlag(MethodDescVersioningStateFlags.IsDefaultVersionActiveChildFlag); - } - else if (nativeCodeVersion.CodeVersionNodeAddress != TargetPointer.Null) - { - uint flags = _target.Read(nativeCodeVersion.CodeVersionNodeAddress + /* NativeCodVersionNode::Flags offset*/) - return ((NativeCodeVersionNodeFlags)flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); - } - else - { - throw new ArgumentException("Invalid NativeCodeVersionHandle"); - } -} - -NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress) -{ - TargetNUInt ilVersionId = GetId(ilcodeVersion); - if (methodDefActiveVersion.Module != TargetPointer.Null) - { - NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); - if (IsActiveNativeCodeVersion(provisionalHandle)) - { - return provisionalHandle; - } - } - else - { - // Get the explicit IL code version - Debug.Assert(methodDefActiveVersion.ILCodeVersionNode != TargetPointer.Null); - ilVersionId = _target.ReadNUint(methodDefActiveVersion.ILCodeVersionNode + /* ILCodeVersionNode::VersionId offset */); - } - - // Iterate through versioning state nodes and return the active one, matching any IL code version - Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); - return FindFirstCodeVersion(rts, md, (codeVersion) => - { - return (ilVersionId == codeVersion.ILVersionId) - && ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); - }); -} +public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle); ``` +1. If `ilCodeVersionHandle` is invalid, return invalid. +2. If `ilCodeVersionHandle` is synthetic, the active native code version could be synthetic. Check if the method's synthetic NativeCodeVersion is active. If it is, return that NativeCodeVersion. +3. Search the linked list of NativeCodeVersions for one with the active flag and the relevent ILVersionId. If found return that node. Otherwise return invalid. + ### Determining whether a method descriptor supports code versioning ```csharp 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 5760592e4f7fa..44f96369a08dd 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 @@ -21,19 +21,22 @@ public CodeVersions_1(Target target) ILCodeVersionHandle ICodeVersions.GetActiveILCodeVersion(TargetPointer methodDesc) { // CodeVersionManager::GetActiveILCodeVersion - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); - return FindActiveILCodeVersion(module, methodDefToken); + GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken); + + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); + TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; + TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); + if (ilVersionStateAddress == TargetPointer.Null) + { + return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + } + Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); + return ActiveILCodeVersionHandleFromState(ilState); } ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativeCodeVersionHandle) { // NativeCodeVersion::GetILCodeVersion - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; if (!nativeCodeVersionHandle.Valid) { return ILCodeVersionHandle.Invalid; @@ -43,11 +46,10 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ { // There is only a single synthetic NativeCodeVersion per // method and it must be on the synthetic ILCodeVersion - MethodDescHandle md = rts.GetMethodDescHandle(nativeCodeVersionHandle.MethodDescAddress); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); + GetModuleAndMethodDesc( + nativeCodeVersionHandle.MethodDescAddress, + out TargetPointer module, + out uint methodDefToken); return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); } else @@ -69,12 +71,7 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer methodDesc) { // CodeVersionManager::GetILCodeVersions - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); - TargetPointer mtAddr = rts.GetMethodTable(md); - TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); - TargetPointer module = rts.GetModule(typeHandle); - uint methodDefToken = rts.GetMethodToken(md); + GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken); ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; @@ -170,7 +167,27 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion { return NativeCodeVersionHandle.Invalid; } - return FindActiveNativeCodeVersion(ilCodeVersionHandle, methodDesc); + + TargetNUInt ilVersionId = GetId(ilCodeVersionHandle); + if (!IsExplicit(ilCodeVersionHandle)) + { + // if the ILCodeVersion is synthetic, then check if the active NativeCodeVersion is the synthetic one + NativeCodeVersionHandle provisionalHandle = new(methodDescAddress: methodDesc, codeVersionNodeAddress: TargetPointer.Null); + if (IsActiveNativeCodeVersion(provisionalHandle)) + { + return provisionalHandle; + } + } + + // Iterate through versioning state nodes and return the active one, matching any IL code version + Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + return FindFirstCodeVersion(rts, md, (codeVersion) => + { + return (ilVersionId == codeVersion.ILVersionId) + && ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); + }); + } [Flags] @@ -239,20 +256,6 @@ private static ILCodeVersionHandle ActiveILCodeVersionHandleFromState(Data.ILCod } } - private ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint methodDefinition) - { - // CodeVersionManager::GetActiveILCodeVersion - ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module); - TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; - TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefinition, out var _); - if (ilVersionStateAddress == TargetPointer.Null) - { - return new ILCodeVersionHandle(module, methodDefinition, TargetPointer.Null); - } - Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); - return ActiveILCodeVersionHandleFromState(ilState); - } - [Flags] internal enum NativeCodeVersionNodeFlags : uint { @@ -288,33 +291,14 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion } } - private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle ilcodeVersion, TargetPointer methodDescAddress) + private void GetModuleAndMethodDesc(TargetPointer methodDesc, out TargetPointer module, out uint methodDefToken) { - TargetNUInt ilVersionId = GetId(ilcodeVersion); - if (!IsExplicit(ilcodeVersion)) - { - NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); - if (IsActiveNativeCodeVersion(provisionalHandle)) - { - return provisionalHandle; - } - } - else - { - // Get the explicit IL code version - Debug.Assert(ilcodeVersion.ILCodeVersionNode != TargetPointer.Null); - Data.ILCodeVersionNode ilCodeVersion = _target.ProcessedData.GetOrAdd(ilcodeVersion.ILCodeVersionNode); - ilVersionId = ilCodeVersion.VersionId; - } - - // Iterate through versioning state nodes and return the active one, matching any IL code version - Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); - return FindFirstCodeVersion(rts, md, (codeVersion) => - { - return (ilVersionId == codeVersion.ILVersionId) - && ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild); - }); + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + TargetPointer mtAddr = rts.GetMethodTable(md); + TypeHandle typeHandle = rts.GetTypeHandle(mtAddr); + module = rts.GetModule(typeHandle); + methodDefToken = rts.GetMethodToken(md); } private static bool IsExplicit(ILCodeVersionHandle handle) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index fcaac0d46c264..f8d45e4196204 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -571,7 +571,7 @@ public void GetILCodeVersions_SyntheticAndExplicit(MockTarget.Architecture arch) TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); - var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress, nativeCode: expectedExplicitCodePointer); + var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress); oneMethod.MethodTable = methodTable; oneMethod.RowId = methodRowId; @@ -599,7 +599,7 @@ public void GetILCodeVersions_SyntheticAndExplicit(MockTarget.Architecture arch) [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void GetILCodeVersionFromNativeCodeVersion_SyntheticAndExplicit(MockTarget.Architecture arch) + public void IlToNativeToIlCodeVersion_SyntheticAndExplicit(MockTarget.Architecture arch) { uint methodRowId = 0x25; // arbitrary TargetCodePointer expectedSyntheticCodePointer = new TargetCodePointer(0x0700_abc0); @@ -635,7 +635,7 @@ public void GetILCodeVersionFromNativeCodeVersion_SyntheticAndExplicit(MockTarge TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); - var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress, nativeCode: expectedExplicitCodePointer); + var oneMethod = MockMethodDesc.CreateVersionable(selfAddress: methodDescAddress, methodDescVersioningState: methodDescVersioningStateAddress); oneMethod.MethodTable = methodTable; oneMethod.RowId = methodRowId; @@ -653,6 +653,7 @@ public void GetILCodeVersionFromNativeCodeVersion_SyntheticAndExplicit(MockTarge // Get the explicit ILCodeVersion and assert that it is in the list of ILCodeVersions ILCodeVersionHandle explicitILcodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILcodeVersion)); + Assert.True(explicitILcodeVersion.IsValid); // Find the other ILCodeVersion (synthetic) and assert that it is valid. ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILcodeVersion)); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index 1931c9c367917..a4b2575eb2ac4 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -127,7 +127,7 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe Builder.TargetTestHelpers.WriteNUInt(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.ILVersionId)].Offset, Builder.TargetTestHelpers.PointerSize), ilVersionId); } - public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt explicitILVersion, TargetPointer? firstNode = null) + public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt ilVersion, TargetPointer? firstNode = null) { TargetPointer activeVersionNode = TargetPointer.Null; TargetPointer next = firstNode != null ? firstNode.Value : TargetPointer.Null; @@ -136,7 +136,7 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe TargetPointer node = AddNativeCodeVersionNode(); bool isActive = i == activeIndex; TargetCodePointer nativeCode = isActive ? activeNativeCode : 0; - TargetNUInt ilVersionId = explicitILVersion; + TargetNUInt ilVersionId = ilVersion; FillNativeCodeVersionNode(node, methodDesc, nativeCode, next, isActive, ilVersionId); next = node; if (isActive) From 8ba821c17266a3ab59949dd9f0b6afe11e1405e4 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:46 -0500 Subject: [PATCH 05/11] fixes for simple comments --- docs/design/datacontracts/ReJIT.md | 2 +- .../Contracts/CodeVersions_1.cs | 6 +++--- src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/design/datacontracts/ReJIT.md b/docs/design/datacontracts/ReJIT.md index 787db4e00f4bd..106360c6d9768 100644 --- a/docs/design/datacontracts/ReJIT.md +++ b/docs/design/datacontracts/ReJIT.md @@ -28,7 +28,7 @@ Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | | ProfControlBlock | GlobalEventMask | an `ICorProfiler` `COR_PRF_MONITOR` value | -| ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT id +| ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT ID | ILCodeVersionNode | RejitState | a `RejitFlags` value | Global variables used: 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 44f96369a08dd..7339b7f1309bf 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 @@ -168,7 +168,6 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion return NativeCodeVersionHandle.Invalid; } - TargetNUInt ilVersionId = GetId(ilCodeVersionHandle); if (!IsExplicit(ilCodeVersionHandle)) { // if the ILCodeVersion is synthetic, then check if the active NativeCodeVersion is the synthetic one @@ -182,6 +181,7 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion // Iterate through versioning state nodes and return the active one, matching any IL code version Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); + TargetNUInt ilVersionId = GetId(ilCodeVersionHandle); return FindFirstCodeVersion(rts, md, (codeVersion) => { return (ilVersionId == codeVersion.ILVersionId) @@ -315,7 +315,7 @@ private ILCodeVersionNode AsNode(ILCodeVersionHandle handle) { if (handle.ILCodeVersionNode == TargetPointer.Null) { - throw new NotImplementedException("Synthetic ILCodeVersion does not have a backing node."); + throw new InvalidOperationException("Synthetic ILCodeVersion does not have a backing node."); } return _target.ProcessedData.GetOrAdd(handle.ILCodeVersionNode); @@ -325,7 +325,7 @@ private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle) { if (handle.CodeVersionNodeAddress == TargetPointer.Null) { - throw new NotImplementedException("Synthetic NativeCodeVersion does not have a backing node."); + throw new InvalidOperationException("Synthetic NativeCodeVersion does not have a backing node."); } return _target.ProcessedData.GetOrAdd(handle.CodeVersionNodeAddress); diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 5aabd599ee55c..5c70894e030ae 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -310,8 +310,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes { // Caller wants some reverted rejit versions. Gather reverted rejit version data to return - // Prepare array to populate with rejitids. "+ 1" because GetReJITIDs - // returns all available rejitids, including the rejitid for the one non-reverted + // Returns all available rejitids, including the rejitid for the one non-reverted // current version. List reJitIds = rejitContract.GetRejitIds(_target, methodDescHandle.Address).ToList(); @@ -325,7 +324,6 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode) == reJitIds[(int)i], ILCodeVersionHandle.Invalid); - if (!ilCodeVersion.IsValid || rejitContract.GetRejitId(ilCodeVersion) == activeVersionId) { continue; @@ -339,8 +337,7 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes iRejitDataReverted++; } - // pcNeededRevertedRejitData != NULL as per condition at top of function (cuz rgRevertedRejitData != - // NULL). + // We already checked that pcNeededRevertedRejitData != NULL because rgRevertedRejitData != NULL *pcNeededRevertedRejitData = iRejitDataReverted; } } From d43256f32e13ca0a80a9be8a007cc88ff6e5836f Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:49 -0500 Subject: [PATCH 06/11] move IsExplicit to Handle, change Handle creation to factory function --- docs/design/datacontracts/CodeVersions.md | 36 +++-------------- .../Contracts/ICodeVersions.cs | 27 +++++++++++-- .../Contracts/CodeVersions_1.cs | 40 +++++++------------ .../MockDescriptors/MockDescriptors.ReJIT.cs | 2 +- .../managed/cdacreader/tests/ReJITTests.cs | 6 +-- 5 files changed, 48 insertions(+), 63 deletions(-) diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index c1dd2d70fb2f8..86b62464b9da4 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -7,22 +7,8 @@ This contract encapsulates support for [code versioning](../features/code-versio ```csharp internal readonly struct ILCodeVersionHandle { - internal readonly TargetPointer Module; - internal readonly uint MethodDefinition; - internal readonly TargetPointer ILCodeVersionNode; - internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) - { - if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null) - throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null"); - - if (module != TargetPointer.Null && methodDef == 0) - throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null"); - - Module = module; - MethodDefinition = methodDef; - ILCodeVersionNode = ilCodeVersionNodeAddress; - } public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; } ``` @@ -30,20 +16,8 @@ internal readonly struct ILCodeVersionHandle ```csharp internal struct NativeCodeVersionHandle { - // no public constructors - internal readonly TargetPointer MethodDescAddress; - internal readonly TargetPointer CodeVersionNodeAddress; - internal NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer codeVersionNodeAddress) - { - if (methodDescAddress != TargetPointer.Null && codeVersionNodeAddress != TargetPointer.Null) - { - throw new ArgumentException("Only one of methodDescAddress and codeVersionNodeAddress can be non-null"); - } - MethodDescAddress = methodDescAddress; - CodeVersionNodeAddress = codeVersionNodeAddress; - } - internal static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null); + public bool Valid => MethodDescAddress != TargetPointer.Null || CodeVersionNodeAddress != TargetPointer.Null; } ``` @@ -195,7 +169,7 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); if (!rts.IsVersionable(md)) { - return new NativeCodeVersionHandle(methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); + return NativeCodeVersion.OfSynthetic(methodDescAddress); } else { @@ -210,7 +184,7 @@ NativeCodeVersionHandle GetSpecificNativeCodeVersion(MethodDescHandle md, Target TargetCodePointer firstNativeCode = rts.GetNativeCode(md); if (firstNativeCode == startAddress) { - NativeCodeVersionHandle first = new NativeCodeVersionHandle(md.Address, TargetPointer.Null); + NativeCodeVersionHandle first = NativeCodeVersionHandle.OfSynthetic(md.Address); return first; } @@ -236,7 +210,7 @@ NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, MethodDescH Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd(currentAddress); if (predicate(current)) { - return new NativeCodeVersionHandle(methodDescAddress: TargetPointer.Null, currentAddress); + return NativeCodeVersionHandle.OfExplicit(currentAddress); } currentAddress = current.Next; } 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 9fcc3c0250daf..b983f1575abfa 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 @@ -30,7 +30,7 @@ internal readonly struct ILCodeVersionHandle internal readonly TargetPointer Module; internal readonly uint MethodDefinition; internal readonly TargetPointer ILCodeVersionNode; - internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) + private ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress) { if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null) throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null"); @@ -38,12 +38,25 @@ internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer if (module != TargetPointer.Null && methodDef == 0) throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null"); + if (module == TargetPointer.Null && methodDef != 0) + throw new ArgumentException("MethodDefinition must be zero if Module is null"); + Module = module; MethodDefinition = methodDef; ILCodeVersionNode = ilCodeVersionNodeAddress; } + + // for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md + internal static ILCodeVersionHandle OfExplicit(TargetPointer ilCodeVersionNodeAddress) => + new ILCodeVersionHandle(TargetPointer.Null, 0, ilCodeVersionNodeAddress); + internal static ILCodeVersionHandle OfSynthetic(TargetPointer module, uint methodDef) => + new ILCodeVersionHandle(module, methodDef, TargetPointer.Null); + public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; + + internal bool IsExplicit => ILCodeVersionNode != TargetPointer.Null; } internal readonly struct NativeCodeVersionHandle @@ -51,7 +64,7 @@ internal readonly struct NativeCodeVersionHandle // no public constructors internal readonly TargetPointer MethodDescAddress; internal readonly TargetPointer CodeVersionNodeAddress; - internal NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer codeVersionNodeAddress) + private NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer codeVersionNodeAddress) { if (methodDescAddress != TargetPointer.Null && codeVersionNodeAddress != TargetPointer.Null) { @@ -61,9 +74,17 @@ internal NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer CodeVersionNodeAddress = codeVersionNodeAddress; } - internal static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null); + // for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md + internal static NativeCodeVersionHandle OfExplicit(TargetPointer codeVersionNodeAddress) => + new NativeCodeVersionHandle(TargetPointer.Null, codeVersionNodeAddress); + internal static NativeCodeVersionHandle OfSynthetic(TargetPointer methodDescAddress) => + new NativeCodeVersionHandle(methodDescAddress, TargetPointer.Null); + + public static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null); + public bool Valid => MethodDescAddress != TargetPointer.Null || CodeVersionNodeAddress != TargetPointer.Null; + internal bool IsExplicit => CodeVersionNodeAddress != TargetPointer.Null; } internal readonly struct CodeVersions : ICodeVersions 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 7339b7f1309bf..22da1405dd275 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 @@ -28,7 +28,7 @@ ILCodeVersionHandle ICodeVersions.GetActiveILCodeVersion(TargetPointer methodDes TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); if (ilVersionStateAddress == TargetPointer.Null) { - return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); } Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); return ActiveILCodeVersionHandleFromState(ilState); @@ -42,7 +42,7 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ return ILCodeVersionHandle.Invalid; } - if (!IsExplicit(nativeCodeVersionHandle)) + if (!nativeCodeVersionHandle.IsExplicit) { // There is only a single synthetic NativeCodeVersion per // method and it must be on the synthetic ILCodeVersion @@ -50,7 +50,7 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ nativeCodeVersionHandle.MethodDescAddress, out TargetPointer module, out uint methodDefToken); - return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); } else { @@ -78,7 +78,7 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); // always add the synthetic version - yield return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); + yield return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); // if explicit versions exist, iterate linked list and return them if (ilVersionStateAddress != TargetPointer.Null) @@ -88,7 +88,7 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m while (nodePointer != TargetPointer.Null) { Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd(nodePointer); - yield return new ILCodeVersionHandle(TargetPointer.Null, 0, nodePointer); + yield return ILCodeVersionHandle.OfExplicit(nodePointer); nodePointer = current.Next; } } @@ -113,7 +113,7 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); if (!rts.IsVersionable(md)) { - return new NativeCodeVersionHandle(methodDescAddress, codeVersionNodeAddress: TargetPointer.Null); + return NativeCodeVersionHandle.OfSynthetic(methodDescAddress); } else { @@ -148,7 +148,7 @@ TargetCodePointer ICodeVersions.GetNativeCode(NativeCodeVersionHandle codeVersio throw new ArgumentException("Invalid NativeCodeVersionHandle"); } - if (!IsExplicit(codeVersionHandle)) + if (!codeVersionHandle.IsExplicit) { MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); return _target.Contracts.RuntimeTypeSystem.GetNativeCode(md); @@ -168,10 +168,10 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion return NativeCodeVersionHandle.Invalid; } - if (!IsExplicit(ilCodeVersionHandle)) + if (!ilCodeVersionHandle.IsExplicit) { // if the ILCodeVersion is synthetic, then check if the active NativeCodeVersion is the synthetic one - NativeCodeVersionHandle provisionalHandle = new(methodDescAddress: methodDesc, codeVersionNodeAddress: TargetPointer.Null); + NativeCodeVersionHandle provisionalHandle = NativeCodeVersionHandle.OfSynthetic(methodDescAddress: methodDesc); if (IsActiveNativeCodeVersion(provisionalHandle)) { return provisionalHandle; @@ -202,7 +202,7 @@ private NativeCodeVersionHandle GetSpecificNativeCodeVersion(IRuntimeTypeSystem TargetCodePointer firstNativeCode = rts.GetNativeCode(md); if (firstNativeCode == startAddress) { - NativeCodeVersionHandle first = new NativeCodeVersionHandle(md.Address, TargetPointer.Null); + NativeCodeVersionHandle first = NativeCodeVersionHandle.OfSynthetic(md.Address); return first; } @@ -229,7 +229,7 @@ private NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, Met Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd(currentAddress); if (predicate(current)) { - return new NativeCodeVersionHandle(methodDescAddress: TargetPointer.Null, currentAddress); + return NativeCodeVersionHandle.OfExplicit(currentAddress); } currentAddress = current.Next; } @@ -247,10 +247,10 @@ private static ILCodeVersionHandle ActiveILCodeVersionHandleFromState(Data.ILCod switch ((ILCodeVersionKind)ilState.ActiveVersionKind) { case ILCodeVersionKind.Explicit: - return new ILCodeVersionHandle(module: TargetPointer.Null, methodDef: 0, ilState.ActiveVersionNode); + return ILCodeVersionHandle.OfExplicit(ilState.ActiveVersionNode); case ILCodeVersionKind.Synthetic: case ILCodeVersionKind.Unknown: - return new ILCodeVersionHandle(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef, TargetPointer.Null); + return ILCodeVersionHandle.OfSynthetic(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef); default: throw new InvalidOperationException($"Unknown ILCodeVersionKind {ilState.ActiveVersionKind}"); } @@ -270,7 +270,7 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion throw new ArgumentException("Invalid NativeCodeVersionHandle"); } - if (!IsExplicit(nativeCodeVersion)) + if (!nativeCodeVersion.IsExplicit) { MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress); TargetPointer versioningStateAddress = _target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md); @@ -301,16 +301,6 @@ private void GetModuleAndMethodDesc(TargetPointer methodDesc, out TargetPointer methodDefToken = rts.GetMethodToken(md); } - private static bool IsExplicit(ILCodeVersionHandle handle) - { - return handle.ILCodeVersionNode != TargetPointer.Null; - } - - private static bool IsExplicit(NativeCodeVersionHandle handle) - { - return handle.CodeVersionNodeAddress != TargetPointer.Null; - } - private ILCodeVersionNode AsNode(ILCodeVersionHandle handle) { if (handle.ILCodeVersionNode == TargetPointer.Null) @@ -333,7 +323,7 @@ private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle) private TargetNUInt GetId(ILCodeVersionHandle ilCodeVersionHandle) { - if (!IsExplicit(ilCodeVersionHandle)) + if (!ilCodeVersionHandle.IsExplicit) { // for non explicit ILCodeVersions, id is always 0 return new TargetNUInt(0); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs index 3cf509a273173..05518db99688b 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs @@ -70,7 +70,7 @@ public ILCodeVersionHandle AddExplicitILCodeVersion(TargetNUInt rejitId, RejitFl { TargetPointer codeVersionNode = _codeVersions.AddILCodeVersionNode(TargetPointer.Null, rejitId, (uint)rejitFlags); - return new ILCodeVersionHandle(TargetPointer.Null, 0, codeVersionNode); + return ILCodeVersionHandle.OfExplicit(codeVersionNode); } internal static Dictionary GetTypes(TargetTestHelpers helpers) diff --git a/src/native/managed/cdacreader/tests/ReJITTests.cs b/src/native/managed/cdacreader/tests/ReJITTests.cs index b6575a875ca65..539aaf4898f11 100644 --- a/src/native/managed/cdacreader/tests/ReJITTests.cs +++ b/src/native/managed/cdacreader/tests/ReJITTests.cs @@ -44,7 +44,7 @@ public void GetRejitIdOk(MockTarget.Architecture arch) Dictionary expectedRejitIds = new() { // synthetic ILCodeVersionHandle - { new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), new TargetNUInt(0) }, + { ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), new TargetNUInt(0) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), new TargetNUInt(1) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(2) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(3) } @@ -73,7 +73,7 @@ public void GetRejitStateOk(MockTarget.Architecture arch) Dictionary expectedRejitStates = new() { // synthetic ILCodeVersionHandle - { new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), RejitState.Active }, + { ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), RejitState.Active }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), RejitState.Active }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), RejitState.Requested }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kSuppressParams | MockReJIT.RejitFlags.kStateRequested), RejitState.Requested } @@ -106,7 +106,7 @@ public void GetRejitIdsOk(MockTarget.Architecture arch) List ilCodeVersionHandles = [ // synthetic ILCodeVersionHandle - new ILCodeVersionHandle(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100, TargetPointer.Null), + ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested) ]; From 60e577ea1b4763af8700ee5507deddfdadb26ab6 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:50 -0500 Subject: [PATCH 07/11] simplify handle documentation --- docs/design/datacontracts/CodeVersions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 86b62464b9da4..27f0814c37dd3 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -7,18 +7,18 @@ This contract encapsulates support for [code versioning](../features/code-versio ```csharp internal readonly struct ILCodeVersionHandle { - public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public static ILCodeVersionHandle Invalid; - public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; + public bool IsValid; } ``` ```csharp internal struct NativeCodeVersionHandle { - internal static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null); + internal static NativeCodeVersionHandle Invalid; - public bool Valid => MethodDescAddress != TargetPointer.Null || CodeVersionNodeAddress != TargetPointer.Null; + public bool Valid; } ``` From a76d85bda47f04204d6537d7f2845d34b8c21c83 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:53 -0500 Subject: [PATCH 08/11] use discards --- src/native/managed/cdacreader/tests/CodeVersionsTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index f8d45e4196204..bc92f687672fb 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -566,8 +566,8 @@ public void GetILCodeVersions_SyntheticAndExplicit(MockTarget.Architecture arch) Module = module, }; - (TargetPointer firstNode, TargetPointer activeSyntheticNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); - (firstNode, TargetPointer activeExplicitNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); + (TargetPointer firstNode, _) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); + (firstNode, _) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); @@ -630,8 +630,8 @@ public void IlToNativeToIlCodeVersion_SyntheticAndExplicit(MockTarget.Architectu Module = module, }; - (TargetPointer firstNode, TargetPointer activeSyntheticNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); - (firstNode, TargetPointer activeExplicitNativeNode) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); + (TargetPointer firstNode, _) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedSyntheticCodePointer, new TargetNUInt(0)); + (firstNode, _) = builder.AddNativeCodeVersionNodesForMethod(methodDescAddress, 2, 1, expectedExplicitCodePointer, ilVersionId, firstNode); TargetPointer methodDescVersioningStateAddress = builder.AddMethodDescVersioningState(nativeCodeVersionNode: firstNode, isDefaultVersionActive: false); From 5aa14150f3acb3758104d8ce5c6b0cfce159b2a7 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:54 -0500 Subject: [PATCH 09/11] make parameter not optional --- src/native/managed/cdacreader/tests/ReJITTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/native/managed/cdacreader/tests/ReJITTests.cs b/src/native/managed/cdacreader/tests/ReJITTests.cs index 539aaf4898f11..7d7201cea5661 100644 --- a/src/native/managed/cdacreader/tests/ReJITTests.cs +++ b/src/native/managed/cdacreader/tests/ReJITTests.cs @@ -17,11 +17,9 @@ public class ReJITTests { internal static Target CreateTarget( MockTarget.Architecture arch, - MockReJIT builder = null, + MockReJIT builder, Mock mockCodeVersions = null) { - builder ??= new MockReJIT(arch); - TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types, builder.Globals); mockCodeVersions ??= new Mock(); From 541066df4744960f91c7b4ae3d8d4f34fc0b886b Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Mon, 2 Dec 2024 13:46:55 -0500 Subject: [PATCH 10/11] explicitILcodeVersion -> explicitILCodeVersion --- .../cdacreader/tests/CodeVersionsTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index bc92f687672fb..26e8859352787 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -587,12 +587,12 @@ public void GetILCodeVersions_SyntheticAndExplicit(MockTarget.Architecture arch) Assert.Equal(2, ilCodeVersions.Count); // Get the explicit ILCodeVersion and assert that it is in the list of ILCodeVersions - ILCodeVersionHandle explicitILcodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); - Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILcodeVersion)); - Assert.Equal(expectedExplicitCodePointer, codeVersions.GetNativeCode(codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILcodeVersion))); + ILCodeVersionHandle explicitILCodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); + Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILCodeVersion)); + Assert.Equal(expectedExplicitCodePointer, codeVersions.GetNativeCode(codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILCodeVersion))); // Find the other ILCodeVersion (synthetic) and assert that it is valid. - ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILcodeVersion)); + ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILCodeVersion)); Assert.True(syntheticILcodeVersion.IsValid); Assert.Equal(expectedSyntheticCodePointer, codeVersions.GetNativeCode(codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion))); } @@ -651,17 +651,17 @@ public void IlToNativeToIlCodeVersion_SyntheticAndExplicit(MockTarget.Architectu Assert.Equal(2, ilCodeVersions.Count); // Get the explicit ILCodeVersion and assert that it is in the list of ILCodeVersions - ILCodeVersionHandle explicitILcodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); - Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILcodeVersion)); - Assert.True(explicitILcodeVersion.IsValid); + ILCodeVersionHandle explicitILCodeVersion = codeVersions.GetActiveILCodeVersion(methodDescAddress); + Assert.Contains(ilCodeVersions, ilcodeVersion => ilcodeVersion.Equals(explicitILCodeVersion)); + Assert.True(explicitILCodeVersion.IsValid); // Find the other ILCodeVersion (synthetic) and assert that it is valid. - ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILcodeVersion)); + ILCodeVersionHandle syntheticILcodeVersion = ilCodeVersions.Find(ilCodeVersion => !ilCodeVersion.Equals(explicitILCodeVersion)); Assert.True(syntheticILcodeVersion.IsValid); // Verify getting ILCode is equal to ILCode from NativeCode from ILCode. - NativeCodeVersionHandle explicitNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILcodeVersion); - Assert.True(explicitILcodeVersion.Equals(codeVersions.GetILCodeVersion(explicitNativeCodeVersion))); + NativeCodeVersionHandle explicitNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, explicitILCodeVersion); + Assert.True(explicitILCodeVersion.Equals(codeVersions.GetILCodeVersion(explicitNativeCodeVersion))); NativeCodeVersionHandle syntheticNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion); Assert.True(syntheticILcodeVersion.Equals(codeVersions.GetILCodeVersion(syntheticNativeCodeVersion))); From b4c776653012c60c4d7c96deed2bf5b6bead5b89 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 3 Dec 2024 12:07:33 -0500 Subject: [PATCH 11/11] address comments --- .../Contracts/ICodeVersions.cs | 12 +++++------ .../Contracts/CodeVersions_1.cs | 20 +++++++++---------- .../Contracts/ReJIT_1.cs | 6 +++--- .../cdacreader/src/Legacy/SOSDacImpl.cs | 6 +++--- .../MockDescriptors/MockDescriptors.ReJIT.cs | 2 +- .../managed/cdacreader/tests/ReJITTests.cs | 12 +++++------ 6 files changed, 29 insertions(+), 29 deletions(-) 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 b983f1575abfa..a6db692f7dede 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 @@ -47,12 +47,12 @@ private ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer } // for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md - internal static ILCodeVersionHandle OfExplicit(TargetPointer ilCodeVersionNodeAddress) => + internal static ILCodeVersionHandle CreateExplicit(TargetPointer ilCodeVersionNodeAddress) => new ILCodeVersionHandle(TargetPointer.Null, 0, ilCodeVersionNodeAddress); - internal static ILCodeVersionHandle OfSynthetic(TargetPointer module, uint methodDef) => + internal static ILCodeVersionHandle CreateSynthetic(TargetPointer module, uint methodDef) => new ILCodeVersionHandle(module, methodDef, TargetPointer.Null); - public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null); + public static ILCodeVersionHandle Invalid { get; } = new(TargetPointer.Null, 0, TargetPointer.Null); public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null; @@ -75,12 +75,12 @@ private NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer c } // for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md - internal static NativeCodeVersionHandle OfExplicit(TargetPointer codeVersionNodeAddress) => + internal static NativeCodeVersionHandle CreateExplicit(TargetPointer codeVersionNodeAddress) => new NativeCodeVersionHandle(TargetPointer.Null, codeVersionNodeAddress); - internal static NativeCodeVersionHandle OfSynthetic(TargetPointer methodDescAddress) => + internal static NativeCodeVersionHandle CreateSynthetic(TargetPointer methodDescAddress) => new NativeCodeVersionHandle(methodDescAddress, TargetPointer.Null); - public static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null); + public static NativeCodeVersionHandle Invalid { get; } = new(TargetPointer.Null, TargetPointer.Null); public bool Valid => MethodDescAddress != TargetPointer.Null || CodeVersionNodeAddress != TargetPointer.Null; 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 22da1405dd275..a0d06e79349c3 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 @@ -28,7 +28,7 @@ ILCodeVersionHandle ICodeVersions.GetActiveILCodeVersion(TargetPointer methodDes TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); if (ilVersionStateAddress == TargetPointer.Null) { - return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); + return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken); } Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); return ActiveILCodeVersionHandleFromState(ilState); @@ -50,7 +50,7 @@ ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativ nativeCodeVersionHandle.MethodDescAddress, out TargetPointer module, out uint methodDefToken); - return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); + return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken); } else { @@ -78,7 +78,7 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); // always add the synthetic version - yield return ILCodeVersionHandle.OfSynthetic(module, methodDefToken); + yield return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken); // if explicit versions exist, iterate linked list and return them if (ilVersionStateAddress != TargetPointer.Null) @@ -88,7 +88,7 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m while (nodePointer != TargetPointer.Null) { Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd(nodePointer); - yield return ILCodeVersionHandle.OfExplicit(nodePointer); + yield return ILCodeVersionHandle.CreateExplicit(nodePointer); nodePointer = current.Next; } } @@ -113,7 +113,7 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); if (!rts.IsVersionable(md)) { - return NativeCodeVersionHandle.OfSynthetic(methodDescAddress); + return NativeCodeVersionHandle.CreateSynthetic(methodDescAddress); } else { @@ -171,7 +171,7 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion if (!ilCodeVersionHandle.IsExplicit) { // if the ILCodeVersion is synthetic, then check if the active NativeCodeVersion is the synthetic one - NativeCodeVersionHandle provisionalHandle = NativeCodeVersionHandle.OfSynthetic(methodDescAddress: methodDesc); + NativeCodeVersionHandle provisionalHandle = NativeCodeVersionHandle.CreateSynthetic(methodDescAddress: methodDesc); if (IsActiveNativeCodeVersion(provisionalHandle)) { return provisionalHandle; @@ -202,7 +202,7 @@ private NativeCodeVersionHandle GetSpecificNativeCodeVersion(IRuntimeTypeSystem TargetCodePointer firstNativeCode = rts.GetNativeCode(md); if (firstNativeCode == startAddress) { - NativeCodeVersionHandle first = NativeCodeVersionHandle.OfSynthetic(md.Address); + NativeCodeVersionHandle first = NativeCodeVersionHandle.CreateSynthetic(md.Address); return first; } @@ -229,7 +229,7 @@ private NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, Met Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd(currentAddress); if (predicate(current)) { - return NativeCodeVersionHandle.OfExplicit(currentAddress); + return NativeCodeVersionHandle.CreateExplicit(currentAddress); } currentAddress = current.Next; } @@ -247,10 +247,10 @@ private static ILCodeVersionHandle ActiveILCodeVersionHandleFromState(Data.ILCod switch ((ILCodeVersionKind)ilState.ActiveVersionKind) { case ILCodeVersionKind.Explicit: - return ILCodeVersionHandle.OfExplicit(ilState.ActiveVersionNode); + return ILCodeVersionHandle.CreateExplicit(ilState.ActiveVersionNode); case ILCodeVersionKind.Synthetic: case ILCodeVersionKind.Unknown: - return ILCodeVersionHandle.OfSynthetic(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef); + return ILCodeVersionHandle.CreateSynthetic(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef); default: throw new InvalidOperationException($"Unknown ILCodeVersionKind {ilState.ActiveVersionKind}"); } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs index eee7d015f3a7b..6e9e80022a573 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs @@ -52,7 +52,7 @@ bool IReJIT.IsEnabled() RejitState IReJIT.GetRejitState(ILCodeVersionHandle ilCodeVersionHandle) { - if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) + if (!ilCodeVersionHandle.IsExplicit) { // for non explicit ILCodeVersions, ReJITState is always kStateActive return RejitState.Active; @@ -62,7 +62,7 @@ RejitState IReJIT.GetRejitState(ILCodeVersionHandle ilCodeVersionHandle) { RejitFlags.kStateRequested => RejitState.Requested, RejitFlags.kStateActive => RejitState.Active, - _ => throw new NotImplementedException($"Unknown ReJIT state: {ilCodeVersionNode.RejitState}"), + _ => throw new InvalidOperationException($"Unknown ReJIT state: {ilCodeVersionNode.RejitState}"), }; } @@ -81,7 +81,7 @@ private ILCodeVersionNode AsNode(ILCodeVersionHandle ilCodeVersionHandle) { if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) { - throw new NotImplementedException("Synthetic ILCodeVersion does not have a backing node."); + throw new InvalidOperationException("Synthetic ILCodeVersion does not have a backing node."); } return _target.ProcessedData.GetOrAdd(ilCodeVersionHandle.ILCodeVersionNode); diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 5c70894e030ae..e9c9644121eb1 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -318,10 +318,10 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes uint iRejitDataReverted = 0; ILCodeVersionHandle activeVersion = nativeCodeContract.GetActiveILCodeVersion(methodDesc); TargetNUInt activeVersionId = rejitContract.GetRejitId(activeVersion); - for (uint i = 0; (i < reJitIds.Count) && (iRejitDataReverted < cRevertedRejitVersions); i++) + for (int i = 0; (i < reJitIds.Count) && (iRejitDataReverted < cRevertedRejitVersions); i++) { ILCodeVersionHandle ilCodeVersion = nativeCodeContract.GetILCodeVersions(methodDesc) - .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode) == reJitIds[(int)i], + .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode) == reJitIds[i], ILCodeVersionHandle.Invalid); if (!ilCodeVersion.IsValid || rejitContract.GetRejitId(ilCodeVersion) == activeVersionId) @@ -471,7 +471,7 @@ private void CopyNativeCodeVersionToReJitData( flags = DacpReJitData.Flags.kActive; break; default: - Debug.Fail("Unknown SharedRejitInfo state. cDAC should be updated to understand this new state."); + Debug.Fail("Unknown RejitState. cDAC should be updated to understand this new state."); break; } pReJitData->flags = flags; diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs index 05518db99688b..335fbc5905753 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.ReJIT.cs @@ -70,7 +70,7 @@ public ILCodeVersionHandle AddExplicitILCodeVersion(TargetNUInt rejitId, RejitFl { TargetPointer codeVersionNode = _codeVersions.AddILCodeVersionNode(TargetPointer.Null, rejitId, (uint)rejitFlags); - return ILCodeVersionHandle.OfExplicit(codeVersionNode); + return ILCodeVersionHandle.CreateExplicit(codeVersionNode); } internal static Dictionary GetTypes(TargetTestHelpers helpers) diff --git a/src/native/managed/cdacreader/tests/ReJITTests.cs b/src/native/managed/cdacreader/tests/ReJITTests.cs index 7d7201cea5661..34b47914fd977 100644 --- a/src/native/managed/cdacreader/tests/ReJITTests.cs +++ b/src/native/managed/cdacreader/tests/ReJITTests.cs @@ -35,14 +35,14 @@ internal static Target CreateTarget( [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void GetRejitIdOk(MockTarget.Architecture arch) + public void GetRejitId_SyntheticAndExplicit_Success(MockTarget.Architecture arch) { MockReJIT mockRejit = new MockReJIT(arch); Dictionary expectedRejitIds = new() { // synthetic ILCodeVersionHandle - { ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), new TargetNUInt(0) }, + { ILCodeVersionHandle.CreateSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), new TargetNUInt(0) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), new TargetNUInt(1) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(2) }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kStateRequested), new TargetNUInt(3) } @@ -64,14 +64,14 @@ public void GetRejitIdOk(MockTarget.Architecture arch) [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void GetRejitStateOk(MockTarget.Architecture arch) + public void GetRejitState_SyntheticAndExplicit_Success(MockTarget.Architecture arch) { MockReJIT mockRejit = new MockReJIT(arch); Dictionary expectedRejitStates = new() { // synthetic ILCodeVersionHandle - { ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), RejitState.Active }, + { ILCodeVersionHandle.CreateSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), RejitState.Active }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), RejitState.Active }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested), RejitState.Requested }, { mockRejit.AddExplicitILCodeVersion(new TargetNUInt(3), MockReJIT.RejitFlags.kSuppressParams | MockReJIT.RejitFlags.kStateRequested), RejitState.Requested } @@ -93,7 +93,7 @@ public void GetRejitStateOk(MockTarget.Architecture arch) [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void GetRejitIdsOk(MockTarget.Architecture arch) + public void GetRejitIds_SyntheticAndExplicit_Success(MockTarget.Architecture arch) { MockReJIT mockRejit = new MockReJIT(arch); Mock mockCodeVersions = new Mock(); @@ -104,7 +104,7 @@ public void GetRejitIdsOk(MockTarget.Architecture arch) List ilCodeVersionHandles = [ // synthetic ILCodeVersionHandle - ILCodeVersionHandle.OfSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), + ILCodeVersionHandle.CreateSynthetic(new TargetPointer(/* arbitrary */ 0x100), /* arbitrary */ 100), mockRejit.AddExplicitILCodeVersion(new TargetNUInt(1), MockReJIT.RejitFlags.kStateActive), mockRejit.AddExplicitILCodeVersion(new TargetNUInt(2), MockReJIT.RejitFlags.kStateRequested) ];