diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 990085695f5345..50c4b82e28fd2d 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -340,6 +340,9 @@ struct cdac_data #ifdef HAVE_GCCOVER static constexpr size_t GCCoverageInfo = offsetof(NativeCodeVersionNode, m_gcCover); #endif // HAVE_GCCOVER +#ifdef FEATURE_TIERED_COMPILATION + static constexpr size_t OptimizationTier = offsetof(NativeCodeVersionNode, m_optTier); +#endif // FEATURE_TIERED_COMPILATION }; class NativeCodeVersionCollection diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 3e7993b5dbb078..300fe56343d51b 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -600,6 +600,7 @@ CDAC_TYPE_INDETERMINATE(MethodDescCodeData) CDAC_TYPE_FIELD(MethodDescCodeData, /*CodePointer*/, TemporaryEntryPoint, offsetof(MethodDescCodeData,TemporaryEntryPoint)) #ifdef FEATURE_CODE_VERSIONING CDAC_TYPE_FIELD(MethodDescCodeData, /*pointer*/, VersioningState, offsetof(MethodDescCodeData,VersioningState)) +CDAC_TYPE_FIELD(MethodDescCodeData, /*uint32*/, OptimizationTier, offsetof(MethodDescCodeData,OptimizationTier)) #endif // FEATURE_CODE_VERSIONING CDAC_TYPE_END(MethodDescCodeData) @@ -845,6 +846,9 @@ CDAC_TYPE_FIELD(NativeCodeVersionNode, /*nuint*/, ILVersionId, cdac_data::GCCoverageInfo) #endif // HAVE_GCCOVER +#ifdef FEATURE_TIERED_COMPILATION +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*uint32*/, OptimizationTier, cdac_data::OptimizationTier) +#endif // FEATURE_TIERED_COMPILATION CDAC_TYPE_END(NativeCodeVersionNode) CDAC_TYPE_BEGIN(ILCodeVersionNode) @@ -1240,6 +1244,13 @@ CDAC_TYPE_FIELD(WebcilSectionHeader, /*uint32*/, PointerToRawData, offsetof(Webc CDAC_TYPE_END(WebcilSectionHeader) #endif +CDAC_TYPE_BEGIN(EEConfig) +CDAC_TYPE_INDETERMINATE(EEConfig) +CDAC_TYPE_FIELD(EEConfig, /*uint8*/, JitMinOpts, cdac_data::JitMinOpts) +CDAC_TYPE_FIELD(EEConfig, /*uint8*/, GenDebuggable, cdac_data::GenDebuggable) +CDAC_TYPE_FIELD(EEConfig, /*uint32*/, TieredCompilation_DefaultTier, cdac_data::TieredCompilation_DefaultTier) +CDAC_TYPE_END(EEConfig) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -1285,6 +1296,7 @@ CDAC_GLOBAL_STRING(RID, RID_STRING) CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION) +CDAC_GLOBAL_POINTER(EEConfig, &::g_pConfig) CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomainPtr) CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) @@ -1299,6 +1311,7 @@ CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) #undef FRAME_TYPE_NAME CDAC_GLOBAL(MethodDescTokenRemainderBitCount, uint8, METHOD_TOKEN_REMAINDER_BIT_COUNT) +CDAC_GLOBAL_POINTER(CORDebuggerControlFlags, &g_CORDebuggerControlFlags) #if FEATURE_COMINTEROP CDAC_GLOBAL(FeatureCOMInterop, uint8, 1) #else diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index fecb76eb69fb41..28b54665e80ceb 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -96,7 +96,7 @@ class EEConfig bool TieredCompilation_UseCallCountingStubs() const { LIMITED_METHOD_CONTRACT; return fTieredCompilation_UseCallCountingStubs; } DWORD TieredCompilation_DeleteCallCountingStubsAfter() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_DeleteCallCountingStubsAfter; } #endif // FEATURE_TIERED_COMPILATION - DWORD TieredCompilation_DefaultTier() const + DWORD TieredCompilation_DefaultTier() const { LIMITED_METHOD_CONTRACT; return tieredCompilation_DefaultTier; @@ -713,6 +713,16 @@ class EEConfig public: DWORD GetSleepOnExit() { return dwSleepOnExit; } + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t JitMinOpts = offsetof(EEConfig, fJitMinOpts); + static constexpr size_t GenDebuggable = offsetof(EEConfig, fDebuggable); + static constexpr size_t TieredCompilation_DefaultTier = offsetof(EEConfig, tieredCompilation_DefaultTier); }; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs index e406b26df6bc82..8ec9054fa40b23 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ICodeVersions.cs @@ -30,6 +30,8 @@ public interface ICodeVersions : IContract public virtual TargetPointer GetIL(ILCodeVersionHandle ilCodeVersionHandle) => throw new NotImplementedException(); public virtual bool HasDefaultIL(ILCodeVersionHandle ilCodeVersionHandle) => throw new NotImplementedException(); + public virtual NativeCodeVersionOptimizationTier GetOptimizationTier(NativeCodeVersionHandle codeVersionHandle) + => throw new NotImplementedException(); } public readonly struct ILCodeVersionHandle @@ -100,3 +102,14 @@ public static NativeCodeVersionHandle CreateSynthetic(TargetPointer methodDescAd { // throws NotImplementedException for all methods } + +public enum NativeCodeVersionOptimizationTier : uint +{ + OptimizationTier0, + OptimizationTier1, + OptimizationTier1OSR, + OptimizationTierOptimized, + OptimizationTier0Instrumented, + OptimizationTier1Instrumented, + OptimizationTierUnknown = 0xFFFFFFFF +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs index 11593d1b302e1a..d9b39adf4a526c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs @@ -22,6 +22,14 @@ public enum ModuleFlags Tenured = 0x1, // Set once we know for sure the Module will not be freed until the appdomain itself exits EditAndContinue = 0x8, // Edit and Continue is enabled for this module ReflectionEmit = 0x40, // Reflection.Emit was used to create this module + ProfilerDisableOpt = 0x80, // Profiler disabled JIT optimizations when module was loaded +} + +[Flags] +public enum DebuggerAssemblyControlFlags +{ + DACF_USER_OVERRIDE = 0x01, + DACF_ALLOW_JIT_OPTS = 0x02, } [Flags] @@ -75,6 +83,8 @@ public interface ILoader : IContract bool IsProbeExtensionResultValid(ModuleHandle handle) => throw new NotImplementedException(); ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException(); + bool IsReadyToRun(ModuleHandle handle) => throw new NotImplementedException(); + DebuggerAssemblyControlFlags GetDebuggerInfoBits(ModuleHandle handle) => throw new NotImplementedException(); bool TryGetSimpleName(ModuleHandle handle, out string simpleName) => throw new NotImplementedException(); string GetPath(ModuleHandle handle) => throw new NotImplementedException(); string GetFileName(ModuleHandle handle) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index dc5671b7d73dbb..6c5cfde6f26501 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -78,6 +78,12 @@ public enum ArrayFunctionType Constructor = 3 } +[Flags] +public enum DebuggerControlFlag +{ + DBCF_ALLOW_JIT_OPT = 0x0008, +} + public interface IRuntimeTypeSystem : IContract { static string IContract.Name => nameof(RuntimeTypeSystem); @@ -207,6 +213,11 @@ public interface IRuntimeTypeSystem : IContract TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException(); TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + NativeCodeVersionOptimizationTier GetMethodDescOptimizationTier(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); + NativeCodeVersionOptimizationTier GetInitialOptimizationTier(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); + bool IsEligibleForTieredCompilation(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); + bool IsJitOptimizationDisabled(MethodDescHandle methodDescHandle) => throw new NotImplementedException(); #endregion MethodDesc inspection APIs #region FieldDesc inspection APIs TargetPointer GetMTOfEnclosingClass(TargetPointer fieldDescPointer) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 89f9f2e5559378..266437e05ad46e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -135,6 +135,7 @@ public enum DataType PatchpointInfo, PortableEntryPoint, VirtualCallStubManager, + EEConfig, TransitionBlock, DebuggerEval, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index eafeb8137df884..e97ed6e9ed53b1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -73,6 +73,7 @@ public static class Globals public const string SizeOfGenericModeBlock = nameof(SizeOfGenericModeBlock); public const string MethodDescTokenRemainderBitCount = nameof(MethodDescTokenRemainderBitCount); + public const string CORDebuggerControlFlags = nameof(CORDebuggerControlFlags); public const string DirectorySeparator = nameof(DirectorySeparator); public const string ExecutionManagerCodeRangeMapAddress = nameof(ExecutionManagerCodeRangeMapAddress); @@ -154,6 +155,7 @@ public static class Globals public const string HandlesPerBlock = nameof(HandlesPerBlock); public const string BlockInvalid = nameof(BlockInvalid); public const string TotalCpuCount = nameof(TotalCpuCount); + public const string EEConfig = nameof(EEConfig); } public static class FieldNames { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs index fc50c95ffbf16f..1fa1e3ed88b636 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/CodeVersions_1.cs @@ -413,4 +413,31 @@ bool ICodeVersions.HasDefaultIL(ILCodeVersionHandle iLCodeVersionHandle) { return iLCodeVersionHandle.IsExplicit ? AsNode(iLCodeVersionHandle).ILAddress == TargetPointer.Null : true; } + + NativeCodeVersionOptimizationTier ICodeVersions.GetOptimizationTier(NativeCodeVersionHandle codeVersionHandle) + { + if (!codeVersionHandle.Valid) + { + throw new ArgumentException("Invalid NativeCodeVersionHandle"); + } + + if (codeVersionHandle.IsExplicit) + { + NativeCodeVersionNode nativeCodeVersionNode = _target.ProcessedData.GetOrAdd(codeVersionHandle.CodeVersionNodeAddress); + return nativeCodeVersionNode.OptimizationTier is null + ? NativeCodeVersionOptimizationTier.OptimizationTierUnknown + : (NativeCodeVersionOptimizationTier) nativeCodeVersionNode.OptimizationTier; + } + else + { + IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(codeVersionHandle.MethodDescAddress); + NativeCodeVersionOptimizationTier optimizationTier = rtsContract.GetMethodDescOptimizationTier(methodDescHandle); + if (optimizationTier == NativeCodeVersionOptimizationTier.OptimizationTierUnknown) + { + return rtsContract.GetInitialOptimizationTier(methodDescHandle); + } + return optimizationTier; + } + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index 1a80d73c95cc90..205087b405355d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs @@ -14,6 +14,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; { private const string DefaultDomainFriendlyName = "DefaultDomain"; private const uint ASSEMBLY_NOTIFYFLAGS_PROFILER_NOTIFIED = 0x1; // Assembly Notify Flag for profiler notification + private const uint DEBUGGER_INFO_MASK_PRIV = 0x0000FC00; + private const int DEBUGGER_INFO_SHIFT_PRIV = 10; private const ushort MaxWebcilSections = 16; // Must stay in sync with native WEBCIL_MAX_SECTIONS. private enum ModuleFlags_1 : uint @@ -21,6 +23,7 @@ private enum ModuleFlags_1 : uint Tenured = 0x1, // Set once we know for sure the Module will not be freed until the appdomain itself exits EditAndContinue = 0x8, // Edit and Continue is enabled for this module ReflectionEmit = 0x40, // Reflection.Emit was used to create this module + ProfilerDisableOpt = 0x80, // Profiler disabled JIT optimizations when module was loaded } private enum PEImageFlags : uint @@ -366,6 +369,8 @@ private static ModuleFlags GetFlags(Data.Module module) flags |= ModuleFlags.EditAndContinue; if (runtimeFlags.HasFlag(ModuleFlags_1.ReflectionEmit)) flags |= ModuleFlags.ReflectionEmit; + if (runtimeFlags.HasFlag(ModuleFlags_1.ProfilerDisableOpt)) + flags |= ModuleFlags.ProfilerDisableOpt; return flags; } @@ -376,6 +381,18 @@ ModuleFlags ILoader.GetFlags(ModuleHandle handle) return GetFlags(module); } + bool ILoader.IsReadyToRun(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return module.ReadyToRunInfo != TargetPointer.Null; + } + + DebuggerAssemblyControlFlags ILoader.GetDebuggerInfoBits(ModuleHandle handle) + { + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + return (DebuggerAssemblyControlFlags)((module.Flags & DEBUGGER_INFO_MASK_PRIV) >> DEBUGGER_INFO_SHIFT_PRIV); + } + bool ILoader.TryGetSimpleName(ModuleHandle handle, out string simpleName) { simpleName = string.Empty; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index ddeb48a6bde1e7..464887c6f3adbf 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1645,6 +1645,86 @@ TargetPointer IRuntimeTypeSystem.GetGCStressCodeCopy(MethodDescHandle methodDesc return TargetPointer.Null; } + NativeCodeVersionOptimizationTier IRuntimeTypeSystem.GetMethodDescOptimizationTier(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + TargetPointer codeDataAddress = methodDesc.CodeData; + if (codeDataAddress == TargetPointer.Null) + return NativeCodeVersionOptimizationTier.OptimizationTierUnknown; + + Data.MethodDescCodeData codeData = _target.ProcessedData.GetOrAdd(codeDataAddress); + return codeData.OptimizationTier is null + ? NativeCodeVersionOptimizationTier.OptimizationTierUnknown + : (NativeCodeVersionOptimizationTier)codeData.OptimizationTier; + } + + bool IRuntimeTypeSystem.IsEligibleForTieredCompilation(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + return methodDesc.IsEligibleForTieredCompilation; + } + + private bool IsJitOptimizationDisabledForSpecificMethod(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + MethodTable methodTable = GetOrCreateMethodTable(methodDesc); + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(methodTable.Module); + MetadataReader? mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle); + + bool isNoMetadata = methodDesc.Classification == MethodClassification.Dynamic; + bool isMiNoOptimization = false; + if (mdReader is not null) + { + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)methodDesc.Token); + MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); + MethodImplAttributes implAttrs = methodDef.ImplAttributes; + isMiNoOptimization = (implAttrs & MethodImplAttributes.NoOptimization) != 0; + } + else if (!isNoMetadata) + { + throw new InvalidOperationException("Failed to get metadata for method"); + } + + return !isNoMetadata && isMiNoOptimization; + } + + private bool IsJitOptimizationDisabledForAllMethodsInChunk(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + MethodTable methodTable = GetOrCreateMethodTable(methodDesc); + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(methodTable.Module); + DebuggerAssemblyControlFlags debuggerInfoBits = _target.Contracts.Loader.GetDebuggerInfoBits(moduleHandle); + DebuggerControlFlag corDebuggerControlFlags = (DebuggerControlFlag)_target.Read(_target.ReadGlobalPointer(Constants.Globals.CORDebuggerControlFlags)); + TargetPointer eeConfigPtr = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.EEConfig)); + Data.EEConfig eeConfig = _target.ProcessedData.GetOrAdd(eeConfigPtr); + + bool corDebuggerAllowJITOpts = debuggerInfoBits.HasFlag(DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS) + || (corDebuggerControlFlags.HasFlag(DebuggerControlFlag.DBCF_ALLOW_JIT_OPT) + && !debuggerInfoBits.HasFlag(DebuggerAssemblyControlFlags.DACF_USER_OVERRIDE)); + bool profilerDisabledOptimizations = _target.Contracts.Loader.GetFlags(moduleHandle).HasFlag(ModuleFlags.ProfilerDisableOpt); + bool areJITOptimizationsDisabled = !corDebuggerAllowJITOpts || profilerDisabledOptimizations; + + return eeConfig.JitMinOpts || eeConfig.GenDebuggable || areJITOptimizationsDisabled; + } + + bool IRuntimeTypeSystem.IsJitOptimizationDisabled(MethodDescHandle methodDescHandle) + { + return IsJitOptimizationDisabledForAllMethodsInChunk(methodDescHandle) || IsJitOptimizationDisabledForSpecificMethod(methodDescHandle); + } + + NativeCodeVersionOptimizationTier IRuntimeTypeSystem.GetInitialOptimizationTier(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + if (!methodDesc.IsEligibleForTieredCompilation) + { + return NativeCodeVersionOptimizationTier.OptimizationTierOptimized; + } + + TargetPointer eeConfigPtr = _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.EEConfig)); + Data.EEConfig eeConfig = _target.ProcessedData.GetOrAdd(eeConfigPtr); + return (NativeCodeVersionOptimizationTier)eeConfig.TieredCompilation_DefaultTier; + } + private sealed class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries { private readonly RuntimeTypeSystem_1 _rts; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs new file mode 100644 index 00000000000000..6b9afa03a56bcb --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class EEConfig : IData +{ + static EEConfig IData.Create(Target target, TargetPointer address) + => new EEConfig(target, address); + + public EEConfig(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEConfig); + + JitMinOpts = target.Read(address + (ulong)type.Fields[nameof(JitMinOpts)].Offset) != 0; + GenDebuggable = target.Read(address + (ulong)type.Fields[nameof(GenDebuggable)].Offset) != 0; + TieredCompilation_DefaultTier = target.Read(address + (ulong)type.Fields[nameof(TieredCompilation_DefaultTier)].Offset); + } + + public bool JitMinOpts { get; init; } + public bool GenDebuggable { get; init; } + public uint TieredCompilation_DefaultTier { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs index dc76b8981b6101..6cd0d58144c810 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs @@ -13,8 +13,13 @@ public MethodDescCodeData(Target target, TargetPointer address) TemporaryEntryPoint = target.ReadCodePointer(address + (ulong)type.Fields[nameof(TemporaryEntryPoint)].Offset); VersioningState = target.ReadPointer(address + (ulong)type.Fields[nameof(VersioningState)].Offset); + if (type.Fields.ContainsKey(nameof(OptimizationTier))) + { + OptimizationTier = target.Read(address + (ulong)type.Fields[nameof(OptimizationTier)].Offset); + } } public TargetCodePointer TemporaryEntryPoint { get; set; } public TargetPointer VersioningState { get; set; } + public uint? OptimizationTier { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs index e68345e4499d23..55f79823fdf024 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs @@ -21,6 +21,10 @@ public NativeCodeVersionNode(Target target, TargetPointer address) { GCCoverageInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); } + if (type.Fields.ContainsKey(nameof(OptimizationTier))) + { + OptimizationTier = target.Read(address + (ulong)type.Fields[nameof(OptimizationTier)].Offset); + } } public TargetPointer Next { get; init; } @@ -31,4 +35,5 @@ public NativeCodeVersionNode(Target target, TargetPointer address) public TargetNUInt ILVersionId { get; init; } public TargetPointer? GCCoverageInfo { get; init; } + public uint? OptimizationTier { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs index 4fca56fce16a90..fd1b858aae8b09 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -944,12 +944,32 @@ public unsafe partial interface ISOSDacInterface4 int GetClrNotification([In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] arguments, int count, int* pNeeded); }; +public struct DacpTieredVersionData +{ + public enum OptimizationTier + { + Unknown, + MinOptJitted, + Optimized, + QuickJitted, + OptimizedTier1, + ReadyToRun, + OptimizedTier1OSR, + QuickJittedInstrumented, + OptimizedTier1Instrumented, + } + + public ClrDataAddress nativeCodeAddr; + public OptimizationTier optimizationTier; + public ClrDataAddress nativeCodeVersionNodePtr; +} + [GeneratedComInterface] [Guid("127d6abe-6c86-4e48-8e7b-220781c58101")] public unsafe partial interface ISOSDacInterface5 { [PreserveSig] - int GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); + int GetTieredVersions(ClrDataAddress methodDesc, int rejitId, DacpTieredVersionData* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); }; public struct DacpMethodTableCollectibleData diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 33cea4425e5c30..68d0d2dc2fcc64 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -4806,8 +4806,115 @@ int ISOSDacInterface4.GetClrNotification(ClrDataAddress[] arguments, int count, #endregion ISOSDacInterface4 #region ISOSDacInterface5 - int ISOSDacInterface5.GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/ void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) - => _legacyImpl5 is not null ? _legacyImpl5.GetTieredVersions(methodDesc, rejitId, nativeCodeAddrs, cNativeCodeAddrs, pcNativeCodeAddrs) : HResults.E_NOTIMPL; + int ISOSDacInterface5.GetTieredVersions(ClrDataAddress methodDesc, int rejitId, DacpTieredVersionData* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) + { + if (methodDesc == 0 || cNativeCodeAddrs == 0 || pcNativeCodeAddrs == null) + { + return HResults.E_INVALIDARG; + } + + *pcNativeCodeAddrs = 0; + int hr = HResults.S_OK; +#if FEATURE_REJIT + try + { + ILoader loader = _target.Contracts.Loader; + ICodeVersions codeVersions = _target.Contracts.CodeVersions; + IReJIT rejitContract = _target.Contracts.ReJIT; + TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target); + ILCodeVersionHandle ilCodeVersionHandle = codeVersions.GetILCodeVersions(methodDescPtr) + .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode).Value == (ulong)rejitId, ILCodeVersionHandle.Invalid); + + if (!ilCodeVersionHandle.IsValid) + throw new ArgumentException(); + + IRuntimeTypeSystem runtimeTypeSystemContract = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle methodDescHandle = runtimeTypeSystemContract.GetMethodDescHandle(methodDescPtr); + TargetPointer modulePtr = runtimeTypeSystemContract.GetModule(methodDescHandle); + ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + TargetPointer r2rImageBase = TargetPointer.Null; + TargetPointer r2rImageEnd = TargetPointer.Null; + if (loader.IsReadyToRun(moduleHandle) + && loader.TryGetLoadedImageContents(moduleHandle, out r2rImageBase, out uint r2rSize, out _)) + { + r2rImageEnd = r2rImageBase + r2rSize; + } + ClrDataAddress r2rImageBaseAddr = r2rImageBase.toClrDataAddress(_target); + ClrDataAddress r2rImageEndAddr = r2rImageEnd.toClrDataAddress(_target); + + int count = 0; + foreach (NativeCodeVersionHandle nativeCodeVersionHandle in codeVersions.GetNativeCodeVersions(methodDescPtr, ilCodeVersionHandle)) + { + ClrDataAddress nativeCodeAddr = codeVersions.GetNativeCode(nativeCodeVersionHandle).Value; + nativeCodeAddrs[count].nativeCodeAddr = nativeCodeAddr; + nativeCodeAddrs[count].nativeCodeVersionNodePtr = nativeCodeVersionHandle.CodeVersionNodeAddress.ToClrDataAddress(_target); + + if (r2rImageBaseAddr <= nativeCodeAddr && nativeCodeAddr < r2rImageEndAddr) + { + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.ReadyToRun; + } + else if (runtimeTypeSystemContract.IsEligibleForTieredCompilation(methodDescHandle)) + { + switch (codeVersions.GetOptimizationTier(nativeCodeVersionHandle)) + { + default: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Unknown; + break; + case NativeCodeVersionOptimizationTier.OptimizationTier0: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.QuickJitted; + break; + case NativeCodeVersionOptimizationTier.OptimizationTier1: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1; + break; + case NativeCodeVersionOptimizationTier.OptimizationTier1OSR: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1OSR; + break; + case NativeCodeVersionOptimizationTier.OptimizationTierOptimized: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Optimized; + break; + case NativeCodeVersionOptimizationTier.OptimizationTier0Instrumented: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.QuickJittedInstrumented; + break; + case NativeCodeVersionOptimizationTier.OptimizationTier1Instrumented: + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1Instrumented; + break; + } + } + else if (runtimeTypeSystemContract.IsJitOptimizationDisabled(methodDescHandle)) + { + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.MinOptJitted; + } + else + { + nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Optimized; + } + + count++; + + if (count >= cNativeCodeAddrs) + { + hr = HResults.S_FALSE; + break; + } + } + + *pcNativeCodeAddrs = count; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl5 is not null) + { + int hrLocal = _legacyImpl5.GetTieredVersions(methodDesc, rejitId, nativeCodeAddrs, cNativeCodeAddrs, pcNativeCodeAddrs); + Debug.ValidateHResult(hr, hrLocal); + } +#endif // DEBUG +#endif // FEATURE_REJIT + return hr; + } #endregion ISOSDacInterface5 #region ISOSDacInterface6 diff --git a/src/native/managed/cdac/tests/CodeVersionsTests.cs b/src/native/managed/cdac/tests/CodeVersionsTests.cs index 7ceaeea2433ef4..5acd3e08e10377 100644 --- a/src/native/managed/cdac/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdac/tests/CodeVersionsTests.cs @@ -722,4 +722,121 @@ public void GetGCStressCodeCopy_NotNull(MockTarget.Architecture arch) { GetGCStressCodeCopy_Impl(arch, returnsNull: false); } + + public static IEnumerable GetOptimizationTierValues() + { + foreach (var archData in new MockTarget.StdArch()) + { + var arch = (MockTarget.Architecture)archData[0]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTier0]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTier1]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTier1OSR]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTierOptimized]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTier0Instrumented]; + yield return [arch, NativeCodeVersionOptimizationTier.OptimizationTier1Instrumented]; + } + } + + [Theory] + [MemberData(nameof(GetOptimizationTierValues))] + public void GetOptimizationTier_Explicit(MockTarget.Architecture arch, NativeCodeVersionOptimizationTier expectedTier) + { + MockCodeVersions builder = new(arch); + + TargetPointer nativeCodeVersionNode = builder.AddNativeCodeVersionNode(); + builder.FillNativeCodeVersionNode( + nativeCodeVersionNode, + methodDesc: new TargetPointer(0x1a0a_0000), + nativeCode: new TargetCodePointer(0x0a0a_0000), + next: TargetPointer.Null, + isActive: true, + ilVersionId: new(1), + optimizationTier: (uint)expectedTier); + + var target = CreateTarget(arch, builder); + var codeVersions = target.Contracts.CodeVersions; + + NativeCodeVersionHandle handle = NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode); + NativeCodeVersionOptimizationTier tier = codeVersions.GetOptimizationTier(handle); + Assert.Equal(expectedTier, tier); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetOptimizationTier_Explicit_MissingField(MockTarget.Architecture arch) + { + MockCodeVersions builder = new(arch); + + TargetPointer nativeCodeVersionNode = builder.AddNativeCodeVersionNode(); + builder.FillNativeCodeVersionNode( + nativeCodeVersionNode, + methodDesc: new TargetPointer(0x1a0a_0000), + nativeCode: new TargetCodePointer(0x0a0a_0000), + next: TargetPointer.Null, + isActive: true, + ilVersionId: new(1)); + + // Remove the OptimizationTier field from the type info to simulate a runtime + // that doesn't expose it (e.g. FEATURE_TIERED_COMPILATION disabled). + var typesWithoutTier = new Dictionary(builder.Types); + var ncvnType = typesWithoutTier[DataType.NativeCodeVersionNode]; + var fieldsWithoutTier = new Dictionary(ncvnType.Fields); + fieldsWithoutTier.Remove(nameof(Data.NativeCodeVersionNode.OptimizationTier)); + typesWithoutTier[DataType.NativeCodeVersionNode] = ncvnType with { Fields = fieldsWithoutTier }; + + TestPlaceholderTarget target = new TestPlaceholderTarget(arch, builder.Builder.GetMemoryContext().ReadFromTarget, typesWithoutTier); + IContractFactory cvfactory = new CodeVersionsFactory(); + ContractRegistry reg = Mock.Of( + c => c.CodeVersions == cvfactory.CreateContract(target, 1) + && c.RuntimeTypeSystem == Mock.Of() + && c.ExecutionManager == Mock.Of() + && c.Loader == Mock.Of()); + target.SetContracts(reg); + + var codeVersions = target.Contracts.CodeVersions; + NativeCodeVersionHandle handle = NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode); + NativeCodeVersionOptimizationTier tier = codeVersions.GetOptimizationTier(handle); + Assert.Equal(NativeCodeVersionOptimizationTier.OptimizationTierUnknown, tier); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetOptimizationTier_Synthetic_DelegatesToRuntimeTypeSystem(MockTarget.Architecture arch) + { + MockCodeVersions builder = new(arch); + TargetPointer methodDescAddress = new(0x1a0a_0000); + + Mock mockRTS = new(); + MethodDescHandle mdHandle = new(methodDescAddress); + mockRTS.Setup(r => r.GetMethodDescHandle(methodDescAddress)).Returns(mdHandle); + mockRTS.Setup(r => r.GetMethodDescOptimizationTier(mdHandle)).Returns(NativeCodeVersionOptimizationTier.OptimizationTierOptimized); + + var target = CreateTarget(arch, builder, mockRuntimeTypeSystem: mockRTS); + var codeVersions = target.Contracts.CodeVersions; + + NativeCodeVersionHandle handle = NativeCodeVersionHandle.CreateSynthetic(methodDescAddress); + NativeCodeVersionOptimizationTier tier = codeVersions.GetOptimizationTier(handle); + Assert.Equal(NativeCodeVersionOptimizationTier.OptimizationTierOptimized, tier); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetOptimizationTier_Synthetic_FallsBackToInitialTier(MockTarget.Architecture arch) + { + MockCodeVersions builder = new(arch); + TargetPointer methodDescAddress = new(0x1a0a_0000); + + Mock mockRTS = new(); + MethodDescHandle mdHandle = new(methodDescAddress); + mockRTS.Setup(r => r.GetMethodDescHandle(methodDescAddress)).Returns(mdHandle); + mockRTS.Setup(r => r.GetMethodDescOptimizationTier(mdHandle)).Returns(NativeCodeVersionOptimizationTier.OptimizationTierUnknown); + mockRTS.Setup(r => r.GetInitialOptimizationTier(mdHandle)).Returns(NativeCodeVersionOptimizationTier.OptimizationTier0); + + var target = CreateTarget(arch, builder, mockRuntimeTypeSystem: mockRTS); + var codeVersions = target.Contracts.CodeVersions; + + NativeCodeVersionHandle handle = NativeCodeVersionHandle.CreateSynthetic(methodDescAddress); + NativeCodeVersionOptimizationTier tier = codeVersions.GetOptimizationTier(handle); + Assert.Equal(NativeCodeVersionOptimizationTier.OptimizationTier0, tier); + } } diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index feee26fc9c24f8..76b691b2052604 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -35,6 +35,7 @@ public class CodeVersions new(nameof(Data.NativeCodeVersionNode.Flags), DataType.uint32), new(nameof(Data.NativeCodeVersionNode.ILVersionId), DataType.nuint), new(nameof(Data.NativeCodeVersionNode.GCCoverageInfo), DataType.pointer), + new(nameof(Data.NativeCodeVersionNode.OptimizationTier), DataType.uint32), ] }; @@ -119,7 +120,7 @@ public TargetPointer AddNativeCodeVersionNode() return fragment.Address; } - public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDesc, TargetCodePointer nativeCode, TargetPointer next, bool isActive, TargetNUInt ilVersionId, TargetPointer? gcCoverageInfo = null) + public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDesc, TargetCodePointer nativeCode, TargetPointer next, bool isActive, TargetNUInt ilVersionId, TargetPointer? gcCoverageInfo = null, uint? optimizationTier = null) { Target.TypeInfo info = Types[DataType.NativeCodeVersionNode]; Span ncvn = Builder.BorrowAddressRange(dest, (int)info.Size!); @@ -129,6 +130,10 @@ public void FillNativeCodeVersionNode(TargetPointer dest, TargetPointer methodDe Builder.TargetTestHelpers.Write(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.Flags)].Offset, sizeof(uint)), isActive ? (uint)CodeVersions_1.NativeCodeVersionNodeFlags.IsActiveChild : 0u); Builder.TargetTestHelpers.WriteNUInt(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.ILVersionId)].Offset, Builder.TargetTestHelpers.PointerSize), ilVersionId); Builder.TargetTestHelpers.WritePointer(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.GCCoverageInfo)].Offset, Builder.TargetTestHelpers.PointerSize), gcCoverageInfo ?? TargetPointer.Null); + if (optimizationTier.HasValue) + { + Builder.TargetTestHelpers.Write(ncvn.Slice(info.Fields[nameof(Data.NativeCodeVersionNode.OptimizationTier)].Offset, sizeof(uint)), optimizationTier.Value); + } } public (TargetPointer First, TargetPointer Active) AddNativeCodeVersionNodesForMethod(TargetPointer methodDesc, int count, int activeIndex, TargetCodePointer activeNativeCode, TargetNUInt ilVersion, TargetPointer? firstNode = null)