From e141af57ae1990147a22c4e6fd753d7fa85acf6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:36:50 +0000 Subject: [PATCH 1/5] Implement SetCompilerFlags on DacDbiImpl using ILoader contract Replace the legacy-delegation stub with a contract-based implementation that: - Gets the ILoader contract and resolves the assembly pointer to a ModuleHandle - Reads current debugger info bits, clears DACF_ALLOW_JIT_OPTS and DACF_ENC_ENABLED, masks with DACF_CONTROL_FLAGS_MASK - Conditionally ORs in the requested flags - Delegates to SetDebuggerInfoBits which handles EncCapable check, JIT optimization disabled state, and EditAndContinue flag logic - Returns CORDBG_S_NOT_ALL_BITS_SET if EnC was requested but could not be enabled - Includes #if DEBUG legacy validation block Add CORDBG_S_NOT_ALL_BITS_SET constant to CorDbgHResults. Add unit tests covering: both flags set (EnC capable), both flags unset, EnC requested but not capable, and JIT opts toggling. Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/0d4cb517-9044-4d94-902e-a91169e2d2f5 Co-authored-by: barosiak <76071368+barosiak@users.noreply.github.com> --- .../CorDbHResults.cs | 1 + .../Dbi/DacDbiImpl.cs | 51 +++++++- src/native/managed/cdac/tests/LoaderTests.cs | 120 ++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) 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.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 6448703ec5be38..81633b1192ba0b 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 @@ -232,7 +232,56 @@ public int GetCompilerFlags(ulong vmAssembly, Interop.BOOL* pfAllowJITOpts, Inte => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetCompilerFlags(vmAssembly, pfAllowJITOpts, pfEnableEnC) : HResults.E_NOTIMPL; 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)); + + // Read the current bits, clear the flags we're about to set, and mask with CONTROL_FLAGS_MASK. + Contracts.DebuggerAssemblyControlFlags dwBits = loader.GetDebuggerInfoBits(handle); + dwBits &= ~(Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED); + dwBits &= Contracts.DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK; + + if (fAllowJitOpts != Interop.BOOL.FALSE) + { + dwBits |= Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS; + } + + if (fEnableEnC != Interop.BOOL.FALSE) + { + dwBits |= Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED; + } + + // SetDebuggerInfoBits handles the EncCapable check, JIT optimization disabled state update, + // and EditAndContinue flag logic internally. + loader.SetDebuggerInfoBits(handle, dwBits); + + // If EnC was requested, check whether the module actually has EditAndContinue set. + // If not, it means the module was not capable of EnC. + if (fEnableEnC != Interop.BOOL.FALSE) + { + Contracts.ModuleFlags flags = loader.GetFlags(handle); + if ((flags & Contracts.ModuleFlags.EditAndContinue) == 0) + { + hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET; + } + } + } + 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/LoaderTests.cs b/src/native/managed/cdac/tests/LoaderTests.cs index 88a18aaee2711a..323439f698a7f9 100644 --- a/src/native/managed/cdac/tests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/LoaderTests.cs @@ -712,3 +712,123 @@ public void SetDebuggerInfoBits_EnablesEnC_ExplicitFlag(MockTarget.Architecture Assert.True((rawFlags & IsEditAndContinue) != 0, "IS_EDIT_AND_CONTINUE should be set when DACF_ENC_ENABLED is explicitly requested"); } } + +public unsafe class SetCompilerFlagsTests +{ + private const uint IsEditAndContinue = 0x00000008; + private const uint IsEncCapable = 0x00000200; + private const uint DebuggerAllowJitOptsPriv = 0x00000800; + + private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWithLoader( + MockTarget.Architecture arch, + Action configure) + { + var targetBuilder = new TestPlaceholderTarget.Builder(arch); + MockLoaderBuilder loader = new(targetBuilder.MemoryBuilder); + + configure(loader, targetBuilder); + + targetBuilder.AddTypes(LoaderTests.CreateContractTypes(loader)); + targetBuilder.AddContract(version: "c1"); + var target = targetBuilder.Build(); + 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; + 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; + 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); + } + + [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_ReturnsNotAllBitsSet(MockTarget.Architecture arch) + { + ulong assemblyAddr = 0; + + var (dacDbi, _) = CreateDacDbiWithLoader(arch, (loader, builder) => + { + var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); + builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); + var module = loader.AddModule(); // Not EnC-capable + assemblyAddr = module.Assembly; + }); + + int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); + + Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); + } + + [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); + } +} From 4b4bb022d63da020e32d0e137f6ee64c5ee6f644 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:38:40 +0000 Subject: [PATCH 2/5] Remove unused variable in SetCompilerFlags_BothFlagsSet_EncCapable test Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/0d4cb517-9044-4d94-902e-a91169e2d2f5 Co-authored-by: barosiak <76071368+barosiak@users.noreply.github.com> --- src/native/managed/cdac/tests/LoaderTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/native/managed/cdac/tests/LoaderTests.cs b/src/native/managed/cdac/tests/LoaderTests.cs index 323439f698a7f9..743862aa10d216 100644 --- a/src/native/managed/cdac/tests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/LoaderTests.cs @@ -740,15 +740,13 @@ private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWit public void SetCompilerFlags_BothFlagsSet_EncCapable(MockTarget.Architecture arch) { ulong assemblyAddr = 0; - int flagsOffset = 0; - var (dacDbi, target) = CreateDacDbiWithLoader(arch, (loader, builder) => + var (dacDbi, _) = 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; - flagsOffset = loader.ModuleLayout.GetField(nameof(Data.Module.Flags)).Offset; }); int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); From 187ffb1f21e39cbce87e24aec98e85981a4a27a6 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Tue, 21 Apr 2026 13:51:30 -0700 Subject: [PATCH 3/5] Code cleanup --- .../Dbi/DacDbiImpl.cs | 30 +++++++------------ src/native/managed/cdac/tests/LoaderTests.cs | 24 +++++++-------- 2 files changed, 23 insertions(+), 31 deletions(-) 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 81633b1192ba0b..a047942b93bcd1 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 @@ -239,34 +239,26 @@ public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Intero Contracts.ILoader loader = _target.Contracts.Loader; Contracts.ModuleHandle handle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly)); - // Read the current bits, clear the flags we're about to set, and mask with CONTROL_FLAGS_MASK. - Contracts.DebuggerAssemblyControlFlags dwBits = loader.GetDebuggerInfoBits(handle); - dwBits &= ~(Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED); - dwBits &= Contracts.DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK; + Contracts.DebuggerAssemblyControlFlags controlFlags = loader.GetDebuggerInfoBits(handle); + controlFlags &= ~(Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED); + controlFlags &= Contracts.DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK; - if (fAllowJitOpts != Interop.BOOL.FALSE) + if (fAllowJitOpts == Interop.BOOL.TRUE) { - dwBits |= Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS; + controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS; } - if (fEnableEnC != Interop.BOOL.FALSE) + if (fEnableEnC == Interop.BOOL.TRUE) { - dwBits |= Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED; + controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED; } - // SetDebuggerInfoBits handles the EncCapable check, JIT optimization disabled state update, - // and EditAndContinue flag logic internally. - loader.SetDebuggerInfoBits(handle, dwBits); + loader.SetDebuggerInfoBits(handle, controlFlags); - // If EnC was requested, check whether the module actually has EditAndContinue set. - // If not, it means the module was not capable of EnC. - if (fEnableEnC != Interop.BOOL.FALSE) + // Check if EnC was requested but the module was not capable. + if (fEnableEnC == Interop.BOOL.TRUE && (loader.GetFlags(handle) & Contracts.ModuleFlags.EditAndContinue) == 0) { - Contracts.ModuleFlags flags = loader.GetFlags(handle); - if ((flags & Contracts.ModuleFlags.EditAndContinue) == 0) - { - hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET; - } + hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET; } } catch (System.Exception ex) diff --git a/src/native/managed/cdac/tests/LoaderTests.cs b/src/native/managed/cdac/tests/LoaderTests.cs index 743862aa10d216..e0149988d48068 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) { @@ -723,14 +723,7 @@ private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWit MockTarget.Architecture arch, Action configure) { - var targetBuilder = new TestPlaceholderTarget.Builder(arch); - MockLoaderBuilder loader = new(targetBuilder.MemoryBuilder); - - configure(loader, targetBuilder); - - targetBuilder.AddTypes(LoaderTests.CreateContractTypes(loader)); - targetBuilder.AddContract(version: "c1"); - var target = targetBuilder.Build(); + var (_, target) = LoaderTests.CreateLoaderContractWithTarget(arch, configure); var dacDbi = new DacDbiImpl(target, legacyObj: null); return (dacDbi, target); } @@ -740,18 +733,25 @@ private static (DacDbiImpl DacDbi, TestPlaceholderTarget Target) CreateDacDbiWit public void SetCompilerFlags_BothFlagsSet_EncCapable(MockTarget.Architecture arch) { ulong assemblyAddr = 0; + TargetPointer moduleAddr = TargetPointer.Null; + int flagsOffset = 0; - var (dacDbi, _) = CreateDacDbiWithLoader(arch, (loader, builder) => + 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(System.HResults.S_OK, hr); + uint rawFlags = target.Read(moduleAddr + (ulong)flagsOffset); + Assert.NotEqual(0u, rawFlags & DebuggerAllowJitOptsPriv); + Assert.NotEqual(0u, rawFlags & IsEditAndContinue); } [Theory] @@ -782,7 +782,7 @@ public void SetCompilerFlags_BothFlagsUnset(MockTarget.Architecture arch) [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void SetCompilerFlags_EnCRequested_NotCapable_ReturnsNotAllBitsSet(MockTarget.Architecture arch) + public void SetCompilerFlags_EnCRequested_NotCapable(MockTarget.Architecture arch) { ulong assemblyAddr = 0; @@ -790,7 +790,7 @@ public void SetCompilerFlags_EnCRequested_NotCapable_ReturnsNotAllBitsSet(MockTa { var config = loader.AddEEConfig((uint)ClrModifiableAssemblies.None); builder.AddGlobals((Constants.Globals.EEConfig, config.Address)); - var module = loader.AddModule(); // Not EnC-capable + var module = loader.AddModule(); assemblyAddr = module.Assembly; }); From 80334b84c0a9af2a428a577dce40eb840c3df6a0 Mon Sep 17 00:00:00 2001 From: Barbara Rosiak Date: Wed, 22 Apr 2026 16:15:50 -0700 Subject: [PATCH 4/5] Update SetCompilerFlags to match native --- docs/design/datacontracts/Loader.md | 1 + docs/design/datacontracts/ReJIT.md | 2 + src/coreclr/inc/cordbpriv.h | 2 +- .../vm/datadescriptor/datadescriptor.inc | 2 + .../Contracts/ILoader.cs | 1 + .../Contracts/Loader_1.cs | 3 + .../Data/ProfControlBlock.cs | 4 + .../Dbi/DacDbiImpl.cs | 37 ++- .../managed/cdac/tests/DacDbiImplTests.cs | 256 ++++++++++++++++++ src/native/managed/cdac/tests/LoaderTests.cs | 118 -------- .../MockDescriptors/MockDescriptors.ReJIT.cs | 18 ++ 11 files changed, 314 insertions(+), 130 deletions(-) create mode 100644 src/native/managed/cdac/tests/DacDbiImplTests.cs 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 be78f9c1bf6028..46d618053a4554 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.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 770f58eb0dc10c..6322eaaaf20a92 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; @@ -322,27 +334,30 @@ public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Intero Contracts.ILoader loader = _target.Contracts.Loader; Contracts.ModuleHandle handle = loader.GetModuleHandleFromAssemblyPtr(new TargetPointer(vmAssembly)); - Contracts.DebuggerAssemblyControlFlags controlFlags = loader.GetDebuggerInfoBits(handle); - controlFlags &= ~(Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS | Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED); - controlFlags &= Contracts.DebuggerAssemblyControlFlags.DACF_CONTROL_FLAGS_MASK; + 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.TRUE) { - controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ALLOW_JIT_OPTS; + controlFlags |= DACF.DACF_ALLOW_JIT_OPTS; } if (fEnableEnC == Interop.BOOL.TRUE) { - controlFlags |= Contracts.DebuggerAssemblyControlFlags.DACF_ENC_ENABLED; + 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); - - // Check if EnC was requested but the module was not capable. - if (fEnableEnC == Interop.BOOL.TRUE && (loader.GetFlags(handle) & Contracts.ModuleFlags.EditAndContinue) == 0) - { - hr = CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET; - } } catch (System.Exception ex) { 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 a78a709da1adec..5150460f4ef698 100644 --- a/src/native/managed/cdac/tests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/LoaderTests.cs @@ -845,121 +845,3 @@ public void GetCompilerFlags(uint rawFlags, Interop.BOOL expectedAllowJITOpts, I Assert.Equal(expectedEnableEnC, enableEnC); } } - -public unsafe class SetCompilerFlagsTests -{ - private const uint IsEditAndContinue = 0x00000008; - private const uint IsEncCapable = 0x00000200; - private const uint DebuggerAllowJitOptsPriv = 0x00000800; - - 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); - 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; - - var (dacDbi, _) = 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; - }); - - int hr = dacDbi.SetCompilerFlags(assemblyAddr, Interop.BOOL.TRUE, Interop.BOOL.TRUE); - - Assert.Equal(CorDbgHResults.CORDBG_S_NOT_ALL_BITS_SET, hr); - } - - [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); - } -} 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; } } From c1bb8fa07b90474ef7ec93a2b027516bf2c1a83b Mon Sep 17 00:00:00 2001 From: Barbara Rosiak <76071368+barosiak@users.noreply.github.com> Date: Thu, 23 Apr 2026 11:03:26 -0700 Subject: [PATCH 5/5] Update BOOL handling Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Dbi/DacDbiImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6322eaaaf20a92..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 @@ -338,12 +338,12 @@ public int SetCompilerFlags(ulong vmAssembly, Interop.BOOL fAllowJitOpts, Intero DACF controlFlags = debuggerInfoBits & ~(DACF.DACF_ALLOW_JIT_OPTS | DACF.DACF_ENC_ENABLED); controlFlags &= DACF.DACF_CONTROL_FLAGS_MASK; - if (fAllowJitOpts == Interop.BOOL.TRUE) + if (fAllowJitOpts != Interop.BOOL.FALSE) { controlFlags |= DACF.DACF_ALLOW_JIT_OPTS; } - if (fEnableEnC == Interop.BOOL.TRUE) + if (fEnableEnC != Interop.BOOL.FALSE) { bool fIgnorePdbs = (debuggerInfoBits & DACF.DACF_IGNORE_PDBS) != 0; bool canSetEnC = (loader.GetFlags(handle) & Contracts.ModuleFlags.EncCapable) != 0 && !CORProfilerPresent() && fIgnorePdbs;