diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index 8f32bdd2e250a7..cefb2d8d5548e4 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -119,6 +119,7 @@ enum DebuggerAssemblyControlFlags : uint DACF_NONE = 0x00, DACF_ALLOW_JIT_OPTS = 0x02, DACF_ENC_ENABLED = 0x08, + DACF_IGNORE_PDBS = 0x20, DACF_CONTROL_FLAGS_MASK = 0x2E, } ``` diff --git a/docs/design/datacontracts/ReJIT.md b/docs/design/datacontracts/ReJIT.md index 38da2cd3b3ed68..e9fa276808abb1 100644 --- a/docs/design/datacontracts/ReJIT.md +++ b/docs/design/datacontracts/ReJIT.md @@ -29,6 +29,8 @@ Data descriptors used: | --- | --- | --- | | ProfControlBlock | GlobalEventMask | an `ICorProfiler` `COR_PRF_MONITOR` value | | ProfControlBlock | RejitOnAttachEnabled | cached value of the `ProfAPI_RejitOnAttach` configuration knob | +| ProfControlBlock | MainProfilerProfInterface | pointer to the main profiler's `ICorProfilerCallback` interface, non-null means a main profiler is attached | +| ProfControlBlock | NotificationProfilerCount | number of notification-only profilers currently attached | | ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT ID | ILCodeVersionNode | RejitState | a `RejitFlags` value | diff --git a/src/coreclr/inc/cordbpriv.h b/src/coreclr/inc/cordbpriv.h index e323937df63ce2..06a3266ec1ec77 100644 --- a/src/coreclr/inc/cordbpriv.h +++ b/src/coreclr/inc/cordbpriv.h @@ -49,7 +49,7 @@ enum DebuggerControlFlag // Flags used to control the debuggable state of modules and // assemblies. // -// [cDAC] [Loader]: Contract depends on DACF_NONE, DACF_ALLOW_JIT_OPTS, DACF_ENC_ENABLED, DACF_CONTROL_FLAGS_MASK. +// [cDAC] [Loader]: Contract depends on DACF_NONE, DACF_ALLOW_JIT_OPTS, DACF_ENC_ENABLED, DACF_IGNORE_PDBS, DACF_CONTROL_FLAGS_MASK. enum DebuggerAssemblyControlFlags { DACF_NONE = 0x00, diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index b7e13c1f8574db..76b02d20f87d35 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -924,6 +924,8 @@ CDAC_TYPE_END(ILCodeVersionNode) CDAC_TYPE_BEGIN(ProfControlBlock) CDAC_TYPE_FIELD(ProfControlBlock, T_UINT64, GlobalEventMask, offsetof(ProfControlBlock, globalEventMask)) CDAC_TYPE_FIELD(ProfControlBlock, T_BOOL, RejitOnAttachEnabled, offsetof(ProfControlBlock, fRejitOnAttachEnabled)) +CDAC_TYPE_FIELD(ProfControlBlock, T_POINTER, MainProfilerProfInterface, offsetof(ProfControlBlock, mainProfilerInfo) + offsetof(ProfilerInfo, pProfInterface)) +CDAC_TYPE_FIELD(ProfControlBlock, T_INT32, NotificationProfilerCount, offsetof(ProfControlBlock, notificationProfilerCount)) CDAC_TYPE_END(ProfControlBlock) #endif // PROFILING_SUPPORTED 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 7ae5e658c17eac..96310c327c262b 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 @@ -33,6 +33,7 @@ public enum DebuggerAssemblyControlFlags : uint DACF_NONE = 0x00, DACF_ALLOW_JIT_OPTS = 0x02, DACF_ENC_ENABLED = 0x08, + DACF_IGNORE_PDBS = 0x20, DACF_CONTROL_FLAGS_MASK = 0x2E, } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs index 34846f9fdaf8ce..63971df217ae9a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/CorDbHResults.cs @@ -10,4 +10,5 @@ public static class CorDbgHResults public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49); public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW) public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303); + public const int CORDBG_S_NOT_ALL_BITS_SET = unchecked((int)0x00131c13); } 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 8b5811e88ed406..cc517fbacb60ee 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 @@ -23,6 +23,7 @@ private enum ModuleFlags_1 : uint JitOptimizationDisabled = 0x2, // Cached flag: JIT optimizations are disabled EditAndContinue = 0x8, // Edit and Continue is enabled for this module ReflectionEmit = 0x40, // Reflection.Emit was used to create this module + EncCapable = 0x200, // Cached flag: module is Edit and Continue capable } private const uint DebuggerInfoMask = 0x0000FC00; @@ -389,6 +390,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.EncCapable)) + flags |= ModuleFlags.EncCapable; return flags; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ProfControlBlock.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ProfControlBlock.cs index e98a6134e3afe2..9f002db3dc045e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ProfControlBlock.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ProfControlBlock.cs @@ -13,8 +13,12 @@ public ProfControlBlock(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.ProfControlBlock); GlobalEventMask = target.ReadField(address, type, nameof(GlobalEventMask)); RejitOnAttachEnabled = target.ReadField(address, type, nameof(RejitOnAttachEnabled)) != 0; + MainProfilerProfInterface = target.ReadPointerField(address, type, nameof(MainProfilerProfInterface)); + NotificationProfilerCount = target.ReadField(address, type, nameof(NotificationProfilerCount)); } public ulong GlobalEventMask { get; init; } public bool RejitOnAttachEnabled { get; init; } + public TargetPointer MainProfilerProfInterface { get; init; } + public int NotificationProfilerCount { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index c1fe7be3d8c101..b08fabbc175a0b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; +using DACF = Microsoft.Diagnostics.DataContractReader.Contracts.DebuggerAssemblyControlFlags; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -37,6 +38,17 @@ private int StringHolderAssignCopy(nint stringHolder, string str) } } + private bool CORProfilerPresent() + { + if (!_target.TryReadGlobalPointer(Constants.Globals.ProfilerControlBlock, out TargetPointer? profControlBlockAddress)) + return false; + + Target.TypeInfo type = _target.GetTypeInfo(DataType.ProfControlBlock); + TargetPointer mainProfInterface = _target.ReadPointerField(profControlBlockAddress.Value, type, "MainProfilerProfInterface"); + int notificationCount = _target.ReadField(profControlBlockAddress.Value, type, "NotificationProfilerCount"); + return mainProfInterface != TargetPointer.Null || notificationCount > 0; + } + public DacDbiImpl(Target target, object? legacyObj) { _target = target; @@ -315,7 +327,51 @@ public int GetCompilerFlags(ulong vmAssembly, Interop.BOOL* pfAllowJITOpts, Inte } public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Interop.BOOL fEnableEnC) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.SetCompilerFlags(vmAssembly, fAllowJitOpts, fEnableEnC) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle handle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly)); + + DACF debuggerInfoBits = loader.GetDebuggerInfoBits(handle); + DACF controlFlags = debuggerInfoBits & ~(DACF.DACF_ALLOW_JIT_OPTS | DACF.DACF_ENC_ENABLED); + controlFlags &= DACF.DACF_CONTROL_FLAGS_MASK; + + if (fAllowJitOpts != Interop.BOOL.FALSE) + { + controlFlags |= DACF.DACF_ALLOW_JIT_OPTS; + } + + if (fEnableEnC != Interop.BOOL.FALSE) + { + bool fIgnorePdbs = (debuggerInfoBits & DACF.DACF_IGNORE_PDBS) != 0; + bool canSetEnC = (loader.GetFlags(handle) & Contracts.ModuleFlags.EncCapable) != 0 && !CORProfilerPresent() && fIgnorePdbs; + if (canSetEnC) + { + controlFlags |= DACF.DACF_ENC_ENABLED; + } + else + { + hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET; + } + } + + loader.SetDebuggerInfoBits(handle, controlFlags); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + int hrLocal = _legacy.SetCompilerFlags(vmAssembly, fAllowJitOpts, fEnableEnC); + Debug.ValidateHResult(hr, hrLocal); + } +#endif + return hr; + } public int EnumerateAssembliesInAppDomain(ulong vmAppDomain, nint fpCallback, nint pUserData) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.EnumerateAssembliesInAppDomain(vmAppDomain, fpCallback, pUserData) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/tests/DacDbiImplTests.cs b/src/native/managed/cdac/tests/DacDbiImplTests.cs new file mode 100644 index 00000000000000..08d6b89b10f318 --- /dev/null +++ b/src/native/managed/cdac/tests/DacDbiImplTests.cs @@ -0,0 +1,256 @@ +// 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; +using Microsoft.Diagnostics.DataContractReader.Legacy; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.Tests; + +public unsafe class DacDbiImplTests +{ + private const uint IsEditAndContinue = 0x00000008; + private const uint IsEncCapable = 0x00000200; + private const uint DebuggerAllowJitOptsPriv = 0x00000800; + private const uint DebuggerEncEnabledPriv = 0x00002000; + private const uint DebuggerIgnorePdbsPriv = 0x00008000; + + private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWithLoader( + MockTarget.Architecture arch, + Action configure) + { + var (_, target) = LoaderTests.CreateLoaderContractWithTarget(arch, configure); + var dacDbi = new DacDbiImpl(target, legacyObj: null); + return (dacDbi, target); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_BothFlagsSet_EncCapable(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.Debug); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(flags: IsEncCapable | DebuggerIgnorePdbsPriv); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(System.HResults.S_OK, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.NotEqual(0u, rawFlags & DebuggerAllowJitOptsPriv); + Assert.NotEqual(0u, rawFlags & IsEditAndContinue); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_BothFlagsUnset(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.FALSE, Interop.BOOL.FALSE); + Assert.Equal(System.HResults.S_OK, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & DebuggerAllowJitOptsPriv); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_EnCRequested_NotCapable(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + Assert.Equal(0u, rawFlags & DebuggerEncEnabledPriv); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_EnCCapable_ModifiableAssembliesNone(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(flags: IsEncCapable | DebuggerIgnorePdbsPriv); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(System.HResults.S_OK, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_EncCapable_NoPdbsIgnored(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.Debug); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(flags: IsEncCapable); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_JitOptsToggling(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + }); + + // Enable JIT opts + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.FALSE); + Assert.Equal(System.HResults.S_OK, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.NotEqual(0u, rawFlags & DebuggerAllowJitOptsPriv); + + // Disable JIT opts + hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.FALSE, Interop.BOOL.FALSE); + Assert.Equal(System.HResults.S_OK, hr); + rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & DebuggerAllowJitOptsPriv); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_EnCBlocked_ProfilerPresent(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var profLayout = MockProfControlBlock.CreateLayout(arch); + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.Debug); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(flags: IsEncCapable | DebuggerIgnorePdbsPriv); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + + var profFragment = builder.MemoryBuilder.CreateAllocator(0x0020_0000, 0x0020_1000).Allocate((ulong)profLayout.Size, "ProfControlBlock"); + MockProfControlBlock profBlock = profLayout.Create(profFragment); + profBlock.GlobalEventMask = 0; + profBlock.RejitOnAttachEnabled = 0; + profBlock.MainProfilerProfInterface = 1; + profBlock.NotificationProfilerCount = 0; + builder.AddGlobals((Constants.Globals.ProfilerControlBlock, profFragment.Address)); + builder.AddTypes(new Dictionary + { + [DataType.ProfControlBlock] = TargetTestHelpers.CreateTypeInfo(profLayout), + }); + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void SetCompilerFlags_EnCBlocked_NotificationProfiler(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; + + var profLayout = MockProfControlBlock.CreateLayout(arch); + var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.Debug); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(flags: IsEncCapable | DebuggerIgnorePdbsPriv); + assemblyAddr = module.Assembly; + moduleAddr = new TargetPointer(module.Address); + flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; + + var profFragment = builder.MemoryBuilder.CreateAllocator(0x0020_0000, 0x0020_1000).Allocate((ulong)profLayout.Size, "ProfControlBlock"); + MockProfControlBlock profBlock = profLayout.Create(profFragment); + profBlock.GlobalEventMask = 0; + profBlock.RejitOnAttachEnabled = 0; + profBlock.MainProfilerProfInterface = 0; + profBlock.NotificationProfilerCount = 2; + builder.AddGlobals((Constants.Globals.ProfilerControlBlock, profFragment.Address)); + builder.AddTypes(new Dictionary + { + [DataType.ProfControlBlock] = TargetTestHelpers.CreateTypeInfo(profLayout), + }); + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.Equal(0u, rawFlags & IsEditAndContinue); + } +} diff --git a/src/native/managed/cdac/tests/LoaderTests.cs b/src/native/managed/cdac/tests/LoaderTests.cs index a78f774c15f2b2..5150460f4ef698 100644 --- a/src/native/managed/cdac/tests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/LoaderTests.cs @@ -43,7 +43,7 @@ private static ILoader CreateLoaderContract(MockTarget.Architecture arch, Action return target.Contracts.Loader; } - private static (ILoader Contract, TestPlaceholderTarget Target) CreateLoaderContractWithTarget( + internal static (ILoader Contract, TestPlaceholderTarget Target) CreateLoaderContractWithTarget( MockTarget.Architecture arch, Action configure) { diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ReJIT.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ReJIT.cs index d4c9feb8e62375..d38ee5a6fe325c 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ReJIT.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ReJIT.cs @@ -9,11 +9,15 @@ internal sealed class MockProfControlBlock : TypedView { private const string GlobalEventMaskFieldName = "GlobalEventMask"; private const string RejitOnAttachEnabledFieldName = "RejitOnAttachEnabled"; + private const string MainProfilerProfInterfaceFieldName = "MainProfilerProfInterface"; + private const string NotificationProfilerCountFieldName = "NotificationProfilerCount"; public static Layout CreateLayout(MockTarget.Architecture architecture) => new SequentialLayoutBuilder("ProfControlBlock", architecture) .AddUInt64Field(GlobalEventMaskFieldName) .AddField(RejitOnAttachEnabledFieldName, sizeof(byte)) + .AddPointerField(MainProfilerProfInterfaceFieldName) + .AddUInt32Field(NotificationProfilerCountFieldName) .Build(); public ulong GlobalEventMask @@ -27,6 +31,18 @@ public byte RejitOnAttachEnabled get => ReadByteField(RejitOnAttachEnabledFieldName); set => WriteByteField(RejitOnAttachEnabledFieldName, value); } + + public ulong MainProfilerProfInterface + { + get => ReadPointerField(MainProfilerProfInterfaceFieldName); + set => WritePointerField(MainProfilerProfInterfaceFieldName, value); + } + + public int NotificationProfilerCount + { + get => (int)ReadUInt32Field(NotificationProfilerCountFieldName); + set => WriteUInt32Field(NotificationProfilerCountFieldName, (uint)value); + } } internal sealed class MockReJITBuilder @@ -86,6 +102,8 @@ private ulong AddProfControlBlock(bool rejitOnAttachEnabled) MockProfControlBlock profControlBlock = ProfControlBlockLayout.Create(fragment); profControlBlock.GlobalEventMask = 0; profControlBlock.RejitOnAttachEnabled = rejitOnAttachEnabled ? (byte)1 : (byte)0; + profControlBlock.MainProfilerProfInterface = 0; + profControlBlock.NotificationProfilerCount = 0; return fragment.Address; } }