diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 103aac455a2a48..cb3406190f4ed3 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -37,6 +37,8 @@ struct CodeBlockHandle void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion); // Gets the offset of the codeInfoHandle inside of the code block TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle); + // Gets information about the EEJitManager: its address, code type, and head of the code heap list. + JitManagerInfo GetEEJitManagerInfo(); // Extension Methods (implemented in terms of other APIs) bool IsFunclet(CodeBlockHandle codeInfoHandle); @@ -70,6 +72,7 @@ Data descriptors used: | `CodeHeapListNode` | `MapBase` | Start of the map - start address rounded down based on OS page size | | `CodeHeapListNode` | `HeaderMap` | Bit array used to find the start of methods - relative to `MapBase` | | `EEJitManager` | `StoreRichDebugInfo` | Boolean value determining if debug info associated with the JitManager contains rich info. | +| `EEJitManager` | `AllCodeHeaps` | Pointer to the head of the linked list of all code heaps managed by the EEJitManager. | | `RealCodeHeader` | `MethodDesc` | Pointer to the corresponding `MethodDesc` | | `RealCodeHeader` | `NumUnwindInfos` | Number of Unwind Infos | | `RealCodeHeader` | `UnwindInfos` | Start address of Unwind Infos | @@ -101,6 +104,7 @@ Global variables used: | Global Name | Type | Purpose | | --- | --- | --- | | `ExecutionManagerCodeRangeMapAddress` | TargetPointer | Pointer to the global RangeSectionMap | +| `EEJitManagerAddress` | TargetPointer | Address of the global pointer to the EEJitManager instance (read a TargetPointer from this address to obtain the instance address) | | `StubCodeBlockLast` | uint8 | Maximum sentinel code header value indentifying a stub code block | | `HashMapSlotsPerBucket` | uint32 | Number of slots in each bucket of a `HashMap` | | `HashMapValueMask` | uint64 | Bitmask used when storing values in a `HashMap` | diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 1f13e6e71c0f92..28c47c49e66fc5 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -2005,6 +2005,8 @@ class EECodeGenManager : public IJitManager Crst m_CodeHeapLock; ULONG m_iteratorCount; bool m_storeRichDebugInfo; + + friend struct ::cdac_data; }; //----------------------------------------------------------------------------- @@ -2263,6 +2265,7 @@ template<> struct cdac_data { static constexpr size_t StoreRichDebugInfo = offsetof(EEJitManager, m_storeRichDebugInfo); + static constexpr size_t AllCodeHeaps = offsetof(EEJitManager, m_pAllCodeHeaps); }; @@ -2580,6 +2583,7 @@ template<> struct cdac_data { static constexpr void* const CodeRangeMapAddress = (void*)&ExecutionManager::g_codeRangeMap.Data[0]; + static constexpr PTR_EEJitManager* EEJitManagerPtr = &ExecutionManager::m_pEEJitManager; }; #endif diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 4799a56f287703..ab42e6019bdb7e 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -672,6 +672,7 @@ CDAC_TYPE_END(RangeSection) CDAC_TYPE_BEGIN(EEJitManager) CDAC_TYPE_INDETERMINATE(EEJitManager) CDAC_TYPE_FIELD(EEJitManager, /*bool*/, StoreRichDebugInfo, cdac_data::StoreRichDebugInfo) +CDAC_TYPE_FIELD(EEJitManager, /*pointer*/, AllCodeHeaps, cdac_data::AllCodeHeaps) CDAC_TYPE_END(EEJitManager) CDAC_TYPE_BEGIN(RealCodeHeader) @@ -1153,6 +1154,7 @@ CDAC_GLOBAL(StressLogMaxMessageSize, uint64, (uint64_t)StressMsg::maxMsgSize) CDAC_GLOBAL(StressLogEnabled, uint8, 0) #endif CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data::CodeRangeMapAddress) +CDAC_GLOBAL_POINTER(EEJitManagerAddress, cdac_data::EEJitManagerPtr) CDAC_GLOBAL_POINTER(PlatformMetadata, &::g_cdacPlatformMetadata) CDAC_GLOBAL_POINTER(ProfilerControlBlock, &::g_profControlBlock) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs index 8a9653e17e7618..6582a36deb8ef5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IExecutionManager.cs @@ -12,6 +12,13 @@ public struct CodeBlockHandle public CodeBlockHandle(TargetPointer address) => Address = address; } +public struct JitManagerInfo +{ + public TargetPointer ManagerAddress; + public uint CodeType; + public TargetPointer HeapListAddress; +} + public interface IExecutionManager : IContract { static string IContract.Name { get; } = nameof(ExecutionManager); @@ -25,6 +32,7 @@ public interface IExecutionManager : IContract // **Currently GetGCInfo only supports X86** void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => throw new NotImplementedException(); TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException(); } public readonly struct ExecutionManager : IExecutionManager 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 68a4ac621aca3a..22eac6f1f968d6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -60,6 +60,7 @@ public static class Globals public const string DirectorySeparator = nameof(DirectorySeparator); public const string ExecutionManagerCodeRangeMapAddress = nameof(ExecutionManagerCodeRangeMapAddress); + public const string EEJitManagerAddress = nameof(EEJitManagerAddress); public const string StubCodeBlockLast = nameof(StubCodeBlockLast); public const string DefaultADID = nameof(DefaultADID); public const string StaticsPointerMask = nameof(StaticsPointerMask); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs index 7161eae75e3840..5577cba635ad58 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.cs @@ -269,4 +269,19 @@ TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle) return info.RelativeOffset; } + + JitManagerInfo IExecutionManager.GetEEJitManagerInfo() + { + TargetPointer eeJitManagerPtr = _target.ReadGlobalPointer(Constants.Globals.EEJitManagerAddress); + TargetPointer eeJitManagerAddr = _target.ReadPointer(eeJitManagerPtr); + + Data.EEJitManager jitManager = _target.ProcessedData.GetOrAdd(eeJitManagerAddr); + + return new JitManagerInfo + { + ManagerAddress = eeJitManagerAddr, + CodeType = 0, // miManaged | miIL + HeapListAddress = jitManager.AllCodeHeaps, + }; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs index 551c664237096c..5ea76dd484d03e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_1.cs @@ -23,4 +23,5 @@ internal ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); + public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs index ab1fda5e329640..f103302d42374e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManager_2.cs @@ -23,4 +23,5 @@ internal ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); public void GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPointer gcInfo, out uint gcVersion) => _executionManagerCore.GetGCInfo(codeInfoHandle, out gcInfo, out gcVersion); public TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetRelativeOffset(codeInfoHandle); + public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEJitManager.cs index 58eae2a2cf67d1..7d2e2d074dd52c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEJitManager.cs @@ -11,7 +11,9 @@ public EEJitManager(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.EEJitManager); StoreRichDebugInfo = target.Read(address + (ulong)type.Fields[nameof(StoreRichDebugInfo)].Offset) != 0; + AllCodeHeaps = target.ReadPointer(address + (ulong)type.Fields[nameof(AllCodeHeaps)].Offset); } public bool StoreRichDebugInfo { get; init; } + public TargetPointer AllCodeHeaps { 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 6851ade98cdffa..63f3ea2ada6052 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ISOSDacInterface.cs @@ -411,6 +411,13 @@ public struct DacpFieldDescData public ClrDataAddress NextField; }; +public struct DacpJitManagerInfo +{ + public ClrDataAddress managerAddr; + public uint codeType; + public ClrDataAddress ptrHeapList; +}; + [GeneratedComInterface] [Guid("436f00f2-b42a-4b9f-870c-e73db66ae930")] public unsafe partial interface ISOSDacInterface @@ -480,7 +487,7 @@ public unsafe partial interface ISOSDacInterface [PreserveSig] int GetCodeHeaderData(ClrDataAddress ip, /*struct DacpCodeHeaderData*/ void* data); [PreserveSig] - int GetJitManagerList(uint count, /*struct DacpJitManagerInfo*/ void* managers, uint* pNeeded); + int GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded); [PreserveSig] int GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded); [PreserveSig] 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 f1ee4780f5968e..a91915f5768369 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -1537,8 +1537,60 @@ int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataA } int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetJitHelperFunctionName(ip, count, name, pNeeded) : HResults.E_NOTIMPL; - int ISOSDacInterface.GetJitManagerList(uint count, void* managers, uint* pNeeded) - => _legacyImpl is not null ? _legacyImpl.GetJitManagerList(count, managers, pNeeded) : HResults.E_NOTIMPL; + int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded) + { + int hr = HResults.S_OK; + try + { + if (managers is not null) + { + if (count >= 1) + { + *managers = default; + Contracts.JitManagerInfo jitManagerInfo = _target.Contracts.ExecutionManager.GetEEJitManagerInfo(); + managers->managerAddr = jitManagerInfo.ManagerAddress.ToClrDataAddress(_target); + managers->codeType = jitManagerInfo.CodeType; + managers->ptrHeapList = jitManagerInfo.HeapListAddress.ToClrDataAddress(_target); + } + } + else if (pNeeded is not null) + { + *pNeeded = 1; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl is not null) + { + if (managers is not null) + { + DacpJitManagerInfo managerLocal = default; + int hrLocal = _legacyImpl.GetJitManagerList(count, &managerLocal, null); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK && count >= 1) + { + Debug.Assert(managers->managerAddr == managerLocal.managerAddr); + Debug.Assert(managers->codeType == managerLocal.codeType); + Debug.Assert(managers->ptrHeapList == managerLocal.ptrHeapList); + } + } + else + { + uint neededLocal; + int hrLocal = _legacyImpl.GetJitManagerList(0, null, &neededLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK && pNeeded is not null) + { + Debug.Assert(*pNeeded == neededLocal); + } + } + } +#endif + return hr; + } private bool IsJumpRel64(TargetPointer pThunk) => 0x48 == _target.Read(pThunk) && diff --git a/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs index b4c858f5a6e573..05e6d96b48533b 100644 --- a/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs @@ -396,6 +396,40 @@ public void GetUnwindInfoBaseAddress_R2R_ManyRuntimeFunction(int version, MockTa Assert.Equal(new TargetPointer(codeRangeStart), actualBaseAddress); } + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetEEJitManagerInfo_ReturnsManagerAddress(int version, MockTarget.Architecture arch) + { + MockDescriptors.ExecutionManager emBuilder = new(version, arch, MockDescriptors.ExecutionManager.DefaultAllocationRange); + var target = CreateTarget(emBuilder); + + var em = target.Contracts.ExecutionManager; + Assert.NotNull(em); + + JitManagerInfo info = em.GetEEJitManagerInfo(); + Assert.Equal(emBuilder.EEJitManagerAddress, info.ManagerAddress); + Assert.Equal(0u, info.CodeType); + Assert.Equal(TargetPointer.Null, info.HeapListAddress); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetEEJitManagerInfo_WithCodeHeaps(int version, MockTarget.Architecture arch) + { + TargetPointer expectedHeapList = new(0x0099_aa00); + + MockDescriptors.ExecutionManager emBuilder = new(version, arch, MockDescriptors.ExecutionManager.DefaultAllocationRange, allCodeHeaps: expectedHeapList); + var target = CreateTarget(emBuilder); + + var em = target.Contracts.ExecutionManager; + Assert.NotNull(em); + + JitManagerInfo info = em.GetEEJitManagerInfo(); + Assert.Equal(emBuilder.EEJitManagerAddress, info.ManagerAddress); + Assert.Equal(0u, info.CodeType); + Assert.Equal(expectedHeapList, info.HeapListAddress); + } + public static IEnumerable StdArchAllVersions() { const int highestVersion = 2; diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs index 85efb7fd191203..c95ee0ff813634 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.ExecutionManager.cs @@ -256,6 +256,16 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect ] }; + private static readonly MockDescriptors.TypeFields EEJitManagerFields = new() + { + DataType = DataType.EEJitManager, + Fields = + [ + new(nameof(Data.EEJitManager.StoreRichDebugInfo), DataType.uint8), + new(nameof(Data.EEJitManager.AllCodeHeaps), DataType.pointer), + ] + }; + internal int Version { get; } internal MockMemorySpace.Builder Builder { get; } @@ -269,11 +279,13 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect private readonly MockMemorySpace.BumpAllocator _nibbleMapAllocator; private readonly MockMemorySpace.BumpAllocator _allocator; - internal ExecutionManager(int version, MockTarget.Architecture arch, AllocationRange allocationRange) - : this(version, new MockMemorySpace.Builder(new TargetTestHelpers(arch)), allocationRange) + internal TargetPointer EEJitManagerAddress { get; } + + internal ExecutionManager(int version, MockTarget.Architecture arch, AllocationRange allocationRange, TargetPointer allCodeHeaps = default) + : this(version, new MockMemorySpace.Builder(new TargetTestHelpers(arch)), allocationRange, allCodeHeaps) { } - internal ExecutionManager(int version, MockMemorySpace.Builder builder, AllocationRange allocationRange) + internal ExecutionManager(int version, MockMemorySpace.Builder builder, AllocationRange allocationRange, TargetPointer allCodeHeaps = default) { Version = version; Builder = builder; @@ -292,14 +304,30 @@ internal ExecutionManager(int version, MockMemorySpace.Builder builder, Allocati RealCodeHeaderFields, ReadyToRunInfoFields(Builder.TargetTestHelpers), MockDescriptors.ModuleFields, + EEJitManagerFields, ]).Concat(MockDescriptors.HashMap.GetTypes(Builder.TargetTestHelpers)) .Concat(_rfBuilder.Types) .ToDictionary(); + // Allocate and populate the EEJitManager instance + var jitManagerTypeInfo = Types[DataType.EEJitManager]; + MockMemorySpace.HeapFragment eeJitManagerFragment = _allocator.Allocate(jitManagerTypeInfo.Size.Value, "EEJitManager"); + Builder.AddHeapFragment(eeJitManagerFragment); + EEJitManagerAddress = eeJitManagerFragment.Address; + int pointerSize = Builder.TargetTestHelpers.PointerSize; + Span jmData = Builder.BorrowAddressRange(eeJitManagerFragment.Address, (int)jitManagerTypeInfo.Size.Value); + Builder.TargetTestHelpers.WritePointer(jmData.Slice(jitManagerTypeInfo.Fields[nameof(Data.EEJitManager.AllCodeHeaps)].Offset, pointerSize), allCodeHeaps); + + // Allocate the global pointer that holds the EEJitManager address + MockMemorySpace.HeapFragment eeJitManagerGlobalPointer = _allocator.Allocate((ulong)pointerSize, "EEJitManagerGlobalPointer"); + Builder.AddHeapFragment(eeJitManagerGlobalPointer); + Builder.TargetTestHelpers.WritePointer(eeJitManagerGlobalPointer.Data, EEJitManagerAddress); + Globals = [ (nameof(Constants.Globals.ExecutionManagerCodeRangeMapAddress), ExecutionManagerCodeRangeMapAddress), (nameof(Constants.Globals.StubCodeBlockLast), 0x0Fu), + (nameof(Constants.Globals.EEJitManagerAddress), eeJitManagerGlobalPointer.Address), ]; Globals = Globals .Concat(MockDescriptors.HashMap.GetGlobals(Builder.TargetTestHelpers))