From 78e0e13cb58c91bb86464274af72cfb07c610382 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Tue, 15 Sep 2026 19:03:59 -0400 Subject: [PATCH] Add interpreter JIT manager code heap support Expose the optional interpreter JIT manager through the ExecutionManager data contract and allow callers to enumerate code heaps by manager kind. Document reader version 2 and cover the behavior with unit and dump tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b507532c-3857-4e61-86a9-69fe22ec157b --- docs/design/datacontracts/ExecutionManager.md | 66 +++++++++++---- docs/design/datacontracts/RuntimeInfo.md | 3 +- .../data-descriptor-meanings.json | 3 +- .../datacontracts/reader-version-history.md | 28 +++++++ src/coreclr/vm/codeman.h | 3 + .../vm/datadescriptor/datadescriptor.inc | 5 +- .../Contracts/IExecutionManager.cs | 10 ++- .../Constants.cs | 1 + .../ExecutionManager/ExecutionManagerCore.cs | 49 +++++++++-- .../ExecutionManager/ExecutionManager_1.cs | 4 +- .../Contracts/RuntimeInfo_1.cs | 2 +- .../SOSDacImpl.cs | 6 +- .../tests/DumpTests/CodeHeapListDumpTests.cs | 14 ++-- .../DumpTests/InterpreterStackDumpTests.cs | 17 ++++ .../ExecutionManager/ExecutionManagerTests.cs | 83 ++++++++++++++++--- .../MockDescriptors.ExecutionManager.cs | 19 ++++- 16 files changed, 258 insertions(+), 55 deletions(-) create mode 100644 docs/design/datacontracts/reader-version-history.md diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 25de45a140dc1c..bfa5b823e7730a 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -42,11 +42,10 @@ public struct CodeBlockHandle TargetNUInt GetRelativeOffset(CodeBlockHandle codeInfoHandle); // Returns true if the instruction pointer is in managed code at a GC-safe point. bool IsGcSafe(TargetCodePointer instructionPointer); - // Gets information about the EEJitManager: its address, code type, and head of the code heap list. - JitManagerInfo GetEEJitManagerInfo(); - // Walks the linked list of CodeHeapListNodes starting from the EEJitManager's AllCodeHeaps head - // and returns information about each code heap. - IEnumerable GetCodeHeapInfos(); + // Gets information about the specified JIT manager, or null when it is not present. + JitManagerInfo? GetJitManagerInfo(JitManagerKind kind); + // Walks the linked list of CodeHeapListNodes for the specified JIT manager. + IEnumerable GetCodeHeapInfos(JitManagerKind kind); // Get the exception clause info for the code block List GetExceptionClauses(CodeBlockHandle codeInfoHandle); @@ -74,6 +73,12 @@ public struct JitManagerInfo public uint CodeType; public TargetPointer HeapListAddress; } + +public enum JitManagerKind +{ + EE, + Interpreter, +} ``` ```csharp @@ -184,7 +189,7 @@ Within a range section fragment, a [nibble map](#nibblemap) structure is used to | `EEExceptionClause` | `TryStartPC` | `uint32` | Native offset of the start of the try block | | `EEExceptionClause` | `TypeHandle` | `nuint` | Union field: TypeHandle (cached), ClassToken, or FilterOffset | | `EEILException` | `Clauses` | `pointer` | Start address of the inline array of `EE_ILEXCEPTION_CLAUSE` entries | -| `EEJitManager` | `AllCodeHeaps` | `pointer` | Pointer to the head of the linked list of all code heaps managed by the EEJitManager. | +| `EEJitManager` | `AllCodeHeaps` | `pointer` | Pointer to the head of the linked list of all code heaps managed by the JIT manager. The field is inherited from EECodeGenManager and has the same offset for EEJitManager and InterpreterJitManager. | | `EEJitManager` | `StoreRichDebugInfo` | `uint8` | Boolean value determining if debug info associated with the JitManager contains rich info. | | `ExceptionLookupTableEntry` | *(type size)* | `uint32` | Size of an exception lookup table entry in bytes | | `ExceptionLookupTableEntry` | `ExceptionInfoRVA` | `uint32` | RVA of the exception clause data | @@ -259,6 +264,7 @@ Within a range section fragment, a [nibble map](#nibblemap) structure is used to | `GCInfoVersion` | `uint32` | JITted code GCInfo version | | `HashMapSlotsPerBucket` | `uint32` | Number of slots in each bucket of a `HashMap` | | `HashMapValueMask` | `uint64` | Bitmask used when storing values in a `HashMap` | +| `InterpreterJitManagerAddress` | `pointer` | Address of the global pointer to the InterpreterJitManager instance. Present only when interpreter support is enabled. | | `ObjectMethodTable` | `pointer` | Address of the global variable holding the System.Object MethodTable pointer | | `StubCodeBlockLast` | `uint8` | Maximum sentinel code header value indentifying a stub code block | | `ThePreStub` | `pointer` | Address of the global containing the prestub entrypoint | @@ -570,20 +576,43 @@ TargetPointer IExecutionManager.FindReadyToRunModule(TargetPointer address) } ``` -### EE JIT Manager and Code Heap Info +### JIT Manager and Code Heap Info + +The optional `InterpreterJitManagerAddress` global identifies the interpreter JIT manager when +interpreter support is enabled. `InterpreterJitManager` inherits its code-heap list from +`EECodeGenManager`, so the `EEJitManager.AllCodeHeaps` descriptor provides the offset for both +manager types. ```csharp -JitManagerInfo IExecutionManager.GetEEJitManagerInfo() +JitManagerInfo? IExecutionManager.GetJitManagerInfo(JitManagerKind kind) { - TargetPointer eeJitManagerPtr = Target.ReadGlobalPointer("EEJitManagerAddress"); - TargetPointer eeJitManagerAddr = Target.ReadPointer(eeJitManagerPtr); - TargetPointer allCodeHeaps = Target.ReadPointer(eeJitManagerAddr + /* EEJitManager::AllCodeHeaps offset */); + TargetPointer jitManagerPtr; + uint codeType; + switch (kind) + { + case JitManagerKind.EE: + jitManagerPtr = Target.ReadGlobalPointer("EEJitManagerAddress"); + codeType = 0; // miManaged | miIL + break; + case JitManagerKind.Interpreter: + if (!Target.TryReadGlobalPointer("InterpreterJitManagerAddress", out jitManagerPtr)) + return null; + codeType = 2; // miManaged | miIL | miOPTIL + break; + default: + throw new ArgumentOutOfRangeException(nameof(kind)); + } + + TargetPointer jitManagerAddr = Target.ReadPointer(jitManagerPtr); + if (jitManagerAddr == TargetPointer.Null) + return null; return new JitManagerInfo { - ManagerAddress = eeJitManagerAddr, - CodeType = 0, // miManaged | miIL - HeapListAddress = allCodeHeaps, + ManagerAddress = jitManagerAddr, + CodeType = codeType, + HeapListAddress = Target.ReadPointer( + jitManagerAddr + /* EECodeGenManager::AllCodeHeaps offset */), }; } @@ -603,10 +632,13 @@ private ICodeHeapInfo GetCodeHeapInfo(TargetPointer codeHeapAddress) }; } -IEnumerable IExecutionManager.GetCodeHeapInfos() +IEnumerable IExecutionManager.GetCodeHeapInfos(JitManagerKind kind) { - TargetPointer heapListHead = GetEEJitManagerInfo().HeapListAddress; - TargetPointer nodeAddr = heapListHead; + JitManagerInfo? jitManagerInfo = GetJitManagerInfo(kind); + if (jitManagerInfo is null) + yield break; + + TargetPointer nodeAddr = jitManagerInfo.Value.HeapListAddress; while (nodeAddr != TargetPointer.Null) { TargetPointer heapAddr = Target.ReadPointer(nodeAddr + /* CodeHeapListNode::Heap offset */); diff --git a/docs/design/datacontracts/RuntimeInfo.md b/docs/design/datacontracts/RuntimeInfo.md index c304744bffc1ee..267d49e91d8f99 100644 --- a/docs/design/datacontracts/RuntimeInfo.md +++ b/docs/design/datacontracts/RuntimeInfo.md @@ -103,4 +103,5 @@ When the .NET runtime team wants to signal that an update is recommended we upda value returned by `GetCurrentReaderVersion()` in the cDAC implementation and the `RecommendedReaderVersion` global value in the runtime. This causes older tools on older cDAC versions to observe `GetRecommendedReaderVersion()` > `GetCurrentReaderVersion()`. The tool can notify the user that an update -is recommended. +is recommended. Changes associated with each reader version are recorded in the +[cDAC reader version history](reader-version-history.md). diff --git a/docs/design/datacontracts/data-descriptor-meanings.json b/docs/design/datacontracts/data-descriptor-meanings.json index 221478b81fe25c..69c08076843c3b 100644 --- a/docs/design/datacontracts/data-descriptor-meanings.json +++ b/docs/design/datacontracts/data-descriptor-meanings.json @@ -140,7 +140,7 @@ "EEExceptionClause.TypeHandle": "Union field: TypeHandle (cached), ClassToken, or FilterOffset", "EEILException.Clauses": "Start address of the inline array of `EE_ILEXCEPTION_CLAUSE` entries", "EEImplMethodDesc.Size": "Base size for mcEEImpl classification", - "EEJitManager.AllCodeHeaps": "Pointer to the head of the linked list of all code heaps managed by the EEJitManager.", + "EEJitManager.AllCodeHeaps": "Pointer to the head of the linked list of all code heaps managed by the JIT manager. The field is inherited from EECodeGenManager and has the same offset for EEJitManager and InterpreterJitManager.", "EEJitManager.StoreRichDebugInfo": "Boolean value determining if debug info associated with the JitManager contains rich info.", "EETypeHashTable.Buckets": "Pointer to the hash table buckets", "EETypeHashTable.Count": "Number of elements in the hash table", @@ -801,6 +801,7 @@ "InitialHandleTableArraySize": "Number of bucket entries in each HandleTableMap", "InterestingDataLength": "The number of elements in the InterestingData array", "InterestingMechanismBitsLength": "The number of elements in the InterestingMechanismBits array", + "InterpreterJitManagerAddress": "Address of the global pointer to the InterpreterJitManager instance. Present only when interpreter support is enabled.", "JITNotificationTable": "Pointer to the g_pNotificationTable array of JITNotification entries", "JITNotificationTableSize": "Maximum number of entries in the notification table (excluding bookkeeping)", "MaxGeneration": "Pointer to the maximum generation number (uint)", diff --git a/docs/design/datacontracts/reader-version-history.md b/docs/design/datacontracts/reader-version-history.md new file mode 100644 index 00000000000000..33d602e9b3d649 --- /dev/null +++ b/docs/design/datacontracts/reader-version-history.md @@ -0,0 +1,28 @@ +# cDAC reader version history + +The cDAC reader version is an advisory value used to indicate when updating a diagnostic tool's +reader is recommended. It is independent of individual data contract versions and does not imply +that an older reader can no longer inspect the runtime. + +The runtime publishes its recommended reader version through the `RecommendedReaderVersion` +global. A reader reports the functionality it understands through +`IRuntimeInfo.GetCurrentReaderVersion()`. A tool can recommend an update when the runtime's +recommended version is greater than the reader's current version. + +## Version 1 + +Initial reader version. + +## Version 2 + +Added support for discovering the interpreter JIT manager and enumerating its code heaps: + +- CoreCLR optionally publishes `InterpreterJitManagerAddress` when built with interpreter support. +- `IExecutionManager.GetJitManagerInfo(JitManagerKind)` returns information for either the EE JIT + manager or the interpreter JIT manager. +- `IExecutionManager.GetCodeHeapInfos(JitManagerKind)` enumerates only the heaps owned by the + selected manager. + +This is an additive data-descriptor change. Version 1 readers ignore the new global and continue +to enumerate EE JIT code heaps. Version 2 readers tolerate the interpreter global being absent and +report no interpreter manager or interpreter code heaps in that case. diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 6e67a78c584d8b..bf48ea087285d8 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -2802,6 +2802,9 @@ struct cdac_data { static constexpr void* const CodeRangeMapAddress = (void*)&ExecutionManager::g_codeRangeMap.Data[0]; static constexpr PTR_EEJitManager* EEJitManagerAddress = &ExecutionManager::m_pEEJitManager; +#ifdef FEATURE_INTERPRETER + static constexpr PTR_InterpreterJitManager* InterpreterJitManagerAddress = &ExecutionManager::m_pInterpreterJitManager; +#endif // FEATURE_INTERPRETER #ifdef TARGET_WASM static constexpr FunctionTableIndexRangeSection** FunctionTableIndexRangeListAddress = &ExecutionManager::s_pFunctionTableIndexRangeList; #endif // TARGET_WASM diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 25c22dc3a928fc..c5021886cc9a86 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -23,7 +23,7 @@ // src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs // to match. #ifndef CDAC_RECOMMENDED_READER_VERSION -#define CDAC_RECOMMENDED_READER_VERSION 1 +#define CDAC_RECOMMENDED_READER_VERSION 2 #endif // When changes will break existing diagnostic tools (changes that are incompatible with the existing data contracts) @@ -1827,6 +1827,9 @@ CDAC_GLOBAL(StressLogEnabled, T_UINT8, 0) #endif CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data::CodeRangeMapAddress) CDAC_GLOBAL_POINTER(EEJitManagerAddress, cdac_data::EEJitManagerAddress) +#ifdef FEATURE_INTERPRETER +CDAC_GLOBAL_POINTER(InterpreterJitManagerAddress, cdac_data::InterpreterJitManagerAddress) +#endif // FEATURE_INTERPRETER #ifdef TARGET_WASM CDAC_GLOBAL_POINTER(FunctionTableIndexRangeList, cdac_data::FunctionTableIndexRangeListAddress) #endif // TARGET_WASM 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 e37ab8ef879c70..ee41ee369afae3 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 @@ -42,6 +42,12 @@ public struct JitManagerInfo public TargetPointer HeapListAddress; } +public enum JitManagerKind +{ + EE, + Interpreter, +} + public enum CodeKind : uint { Unknown = 0, @@ -114,8 +120,8 @@ public interface IExecutionManager : IContract bool IsGcSafe(TargetCodePointer instructionPointer) => throw new NotImplementedException(); List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); uint GetStackParameterSize(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); - JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException(); - IEnumerable GetCodeHeapInfos() => throw new NotImplementedException(); + JitManagerInfo? GetJitManagerInfo(JitManagerKind kind) => throw new NotImplementedException(); + IEnumerable GetCodeHeapInfos(JitManagerKind kind) => throw new NotImplementedException(); IReadOnlyList GetDynamicFunctionTableEntries(TargetPointer tableAddress) => throw new NotImplementedException(); // Classify a code address as a known stub kind (precode, jump stub, VSD stub, etc.) // or as managed code. Returns Unknown if the address is not recognized. 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 8c257267fa5072..a6e0c7b843be23 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -91,6 +91,7 @@ public static class Globals public const string ExecutionManagerCodeRangeMapAddress = nameof(ExecutionManagerCodeRangeMapAddress); public const string EEJitManagerAddress = nameof(EEJitManagerAddress); + public const string InterpreterJitManagerAddress = nameof(InterpreterJitManagerAddress); public const string StubCodeBlockLast = nameof(StubCodeBlockLast); public const string ThePreStub = nameof(ThePreStub); 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 1e7fad5357992c..964f52c4d12edc 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 @@ -457,17 +457,43 @@ TargetPointer IExecutionManager.FindReadyToRunModule(TargetPointer address) return range.Data.R2RModule; } - JitManagerInfo IExecutionManager.GetEEJitManagerInfo() + JitManagerInfo? IExecutionManager.GetJitManagerInfo(JitManagerKind kind) { - TargetPointer eeJitManagerPtr = _target.ReadGlobalPointer(Constants.Globals.EEJitManagerAddress); - TargetPointer eeJitManagerAddr = _target.ReadPointer(eeJitManagerPtr); + return kind switch + { + JitManagerKind.EE => GetJitManagerInfo( + _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.EEJitManagerAddress)), + codeType: 0), // miManaged | miIL + JitManagerKind.Interpreter => GetInterpreterJitManagerInfo(), + _ => throw new ArgumentOutOfRangeException(nameof(kind)), + }; + } + + private JitManagerInfo? GetInterpreterJitManagerInfo() + { + if (!_target.TryReadGlobalPointer( + Constants.Globals.InterpreterJitManagerAddress, + out TargetPointer? interpreterJitManagerPointer) + || interpreterJitManagerPointer is not TargetPointer interpreterJitManagerPointerAddress) + { + return null; + } - Data.EEJitManager jitManager = _target.ProcessedData.GetOrAdd(eeJitManagerAddr); + TargetPointer interpreterJitManagerAddress = _target.ReadPointer(interpreterJitManagerPointerAddress); + return interpreterJitManagerAddress == TargetPointer.Null + ? null + : GetJitManagerInfo( + interpreterJitManagerAddress, + codeType: 2); // miManaged | miIL | miOPTIL + } + private JitManagerInfo GetJitManagerInfo(TargetPointer jitManagerAddress, uint codeType) + { + Data.EEJitManager jitManager = _target.ProcessedData.GetOrAdd(jitManagerAddress); return new JitManagerInfo { - ManagerAddress = eeJitManagerAddr, - CodeType = 0, // miManaged | miIL + ManagerAddress = jitManagerAddress, + CodeType = codeType, HeapListAddress = jitManager.AllCodeHeaps, }; } @@ -486,10 +512,15 @@ private ICodeHeapInfo GetCodeHeapInfo(TargetPointer codeHeapAddress) }; } - IEnumerable IExecutionManager.GetCodeHeapInfos() + IEnumerable IExecutionManager.GetCodeHeapInfos(JitManagerKind kind) { - TargetPointer heapListAddress = ((IExecutionManager)this).GetEEJitManagerInfo().HeapListAddress; - TargetPointer nodeAddr = heapListAddress; + JitManagerInfo? jitManagerInfo = ((IExecutionManager)this).GetJitManagerInfo(kind); + if (jitManagerInfo is not JitManagerInfo info) + { + yield break; + } + + TargetPointer nodeAddr = info.HeapListAddress; while (nodeAddr != TargetPointer.Null) { Data.CodeHeapListNode node = _target.ProcessedData.GetOrAdd(nodeAddr); 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 4058ac5b0a6bbe..8c435ac9ed16c3 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 @@ -32,8 +32,8 @@ internal ExecutionManager_1(Target target) public bool IsGcSafe(TargetCodePointer instructionPointer) => _executionManagerCore.IsGcSafe(instructionPointer); public uint GetStackParameterSize(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetStackParameterSize(codeInfoHandle); public List GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle); - public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo(); - public IEnumerable GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos(); + public JitManagerInfo? GetJitManagerInfo(JitManagerKind kind) => _executionManagerCore.GetJitManagerInfo(kind); + public IEnumerable GetCodeHeapInfos(JitManagerKind kind) => _executionManagerCore.GetCodeHeapInfos(kind); public IReadOnlyList GetDynamicFunctionTableEntries(TargetPointer tableAddress) => _executionManagerCore.GetDynamicFunctionTableEntries(tableAddress); public CodeKind GetCodeKind(TargetCodePointer codeAddress) => _executionManagerCore.GetCodeKind(codeAddress); public TargetPointer FindReadyToRunModule(TargetPointer address) => _executionManagerCore.FindReadyToRunModule(address); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs index 513864f5967313..b981c8ad6fe020 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeInfo_1.cs @@ -41,7 +41,7 @@ RuntimeInfoRuntimeFlavor IRuntimeInfo.GetRuntimeFlavor() string IRuntimeInfo.GetRuntimeProductVersion() => _runtimeProductVersion ??= _target.ReadGlobalString(Constants.Globals.RuntimeProductVersionString); - uint IRuntimeInfo.GetCurrentReaderVersion() => 1; + uint IRuntimeInfo.GetCurrentReaderVersion() => 2; uint IRuntimeInfo.GetRecommendedReaderVersion() => _recommendedReaderVersion ??= ReadRecommendedReaderVersion(); 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 db1f13467746c6..ede4279d218063 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -896,11 +896,11 @@ int ISOSDacInterface.GetCodeHeapList(ClrDataAddress jitManager, uint count, [In, IExecutionManager em = _target.Contracts.ExecutionManager; #if DEBUG - Contracts.JitManagerInfo jitManagerInfo = em.GetEEJitManagerInfo(); + Contracts.JitManagerInfo jitManagerInfo = em.GetJitManagerInfo(JitManagerKind.EE)!.Value; Debug.Assert(jitManager.ToTargetPointer(_target) == jitManagerInfo.ManagerAddress); #endif - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); int i = 0; if (codeHeaps is not null) { @@ -2235,7 +2235,7 @@ int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, if (count >= 1) { *managers = default; - Contracts.JitManagerInfo jitManagerInfo = _target.Contracts.ExecutionManager.GetEEJitManagerInfo(); + Contracts.JitManagerInfo jitManagerInfo = _target.Contracts.ExecutionManager.GetJitManagerInfo(JitManagerKind.EE)!.Value; managers->managerAddr = jitManagerInfo.ManagerAddress.ToClrDataAddress(_target); managers->codeType = jitManagerInfo.CodeType; managers->ptrHeapList = jitManagerInfo.HeapListAddress.ToClrDataAddress(_target); diff --git a/src/native/managed/cdac/tests/DumpTests/CodeHeapListDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/CodeHeapListDumpTests.cs index 5d50e7fc619d96..131683f51c882e 100644 --- a/src/native/managed/cdac/tests/DumpTests/CodeHeapListDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/CodeHeapListDumpTests.cs @@ -28,7 +28,7 @@ public void GetCodeHeapList_ReturnsNonEmptyList(TestConfiguration config) InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); Assert.True(heapInfos.Count > 0, "Expected at least one code heap in the runtime"); } @@ -41,7 +41,7 @@ public void GetCodeHeapList_ContainsLoaderCodeHeap(TestConfiguration config) InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); Assert.Contains(heapInfos, h => h is LoaderCodeHeapInfo); } @@ -54,7 +54,7 @@ public void GetCodeHeapList_ContainsHostCodeHeap(TestConfiguration config) InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); Assert.Contains(heapInfos, h => h is HostCodeHeapInfo); } @@ -67,7 +67,7 @@ public void GetCodeHeapInfo_LoaderCodeHeap_HasNonNullAddress(TestConfiguration c InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); LoaderCodeHeapInfo loader = Assert.IsType(heapInfos.First(h => h is LoaderCodeHeapInfo)); @@ -82,7 +82,7 @@ public void GetCodeHeapInfo_HostCodeHeap_HasValidAddresses(TestConfiguration con InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); HostCodeHeapInfo host = Assert.IsType(heapInfos.First(h => h is HostCodeHeapInfo)); @@ -95,12 +95,12 @@ public void GetCodeHeapInfo_HostCodeHeap_HasValidAddresses(TestConfiguration con [ConditionalTheory] [MemberData(nameof(TestConfigurations))] [SkipOnVersion("net10.0", "Code heap list APIs were added after net10.0")] - public void GetEEJitManagerInfo_ReturnsValidInfo(TestConfiguration config) + public void GetJitManagerInfo_EE_ReturnsValidInfo(TestConfiguration config) { InitializeDumpTest(config); IExecutionManager em = Target.Contracts.ExecutionManager; - JitManagerInfo info = em.GetEEJitManagerInfo(); + JitManagerInfo info = em.GetJitManagerInfo(JitManagerKind.EE)!.Value; Assert.NotEqual(TargetPointer.Null, info.ManagerAddress); Assert.NotEqual(TargetPointer.Null, info.HeapListAddress); diff --git a/src/native/managed/cdac/tests/DumpTests/InterpreterStackDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/InterpreterStackDumpTests.cs index c753e9b3395159..372ed753035a2c 100644 --- a/src/native/managed/cdac/tests/DumpTests/InterpreterStackDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/InterpreterStackDumpTests.cs @@ -70,6 +70,23 @@ private void AssertJitted(ResolvedFrame f) Assert.Equal(CodeKind.Jitted, executionManager.GetCodeKind(nativeCode)); } + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + public void ExecutionManager_EnumeratesInterpreterCodeHeaps(TestConfiguration config) + { + InitializeDumpTest(config); + SkipIfInterpreterNotAvailable(); + + IExecutionManager executionManager = Target.Contracts.ExecutionManager; + JitManagerInfo? managerInfo = executionManager.GetJitManagerInfo(JitManagerKind.Interpreter); + + Assert.NotNull(managerInfo); + Assert.NotEqual(TargetPointer.Null, managerInfo.Value.ManagerAddress); + Assert.Equal(2u, managerInfo.Value.CodeType); // miManaged | miIL | miOPTIL + Assert.NotEqual(TargetPointer.Null, managerInfo.Value.HeapListAddress); + Assert.NotEmpty(executionManager.GetCodeHeapInfos(JitManagerKind.Interpreter)); + } + [ConditionalTheory] [MemberData(nameof(TestConfigurations))] public void StackWalk_VerifyInterleavedStackLayout(TestConfiguration config) diff --git a/src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs index 1c5b92404aa828..fc5a2e8ce4c05d 100644 --- a/src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/ExecutionManager/ExecutionManagerTests.cs @@ -557,14 +557,14 @@ public void GetMethodDesc_CollectibleFragmentNext(string version, MockTarget.Arc [Theory] [MemberData(nameof(StdArchAllVersions))] - public void GetEEJitManagerInfo_ReturnsManagerAddress(string version, MockTarget.Architecture arch) + public void GetJitManagerInfo_EE_ReturnsManagerAddress(string version, MockTarget.Architecture arch) { ulong expectedManagerAddress = 0; IExecutionManager em = CreateExecutionManagerContract( version, arch, emBuilder => expectedManagerAddress = emBuilder.EEJitManagerAddress); - JitManagerInfo info = em.GetEEJitManagerInfo(); + JitManagerInfo info = em.GetJitManagerInfo(JitManagerKind.EE)!.Value; Assert.Equal(new TargetPointer(expectedManagerAddress), info.ManagerAddress); Assert.Equal(0u, info.CodeType); Assert.Equal(TargetPointer.Null, info.HeapListAddress); @@ -572,7 +572,7 @@ public void GetEEJitManagerInfo_ReturnsManagerAddress(string version, MockTarget [Theory] [MemberData(nameof(StdArchAllVersions))] - public void GetEEJitManagerInfo_WithCodeHeaps(string version, MockTarget.Architecture arch) + public void GetJitManagerInfo_EE_WithCodeHeaps(string version, MockTarget.Architecture arch) { const ulong expectedHeapList = 0x0099_aa00; ulong expectedManagerAddress = 0; @@ -581,7 +581,7 @@ public void GetEEJitManagerInfo_WithCodeHeaps(string version, MockTarget.Archite arch, emBuilder => expectedManagerAddress = emBuilder.EEJitManagerAddress, allCodeHeaps: expectedHeapList); - JitManagerInfo info = em.GetEEJitManagerInfo(); + JitManagerInfo info = em.GetJitManagerInfo(JitManagerKind.EE)!.Value; Assert.Equal(new TargetPointer(expectedManagerAddress), info.ManagerAddress); Assert.Equal(0u, info.CodeType); Assert.Equal(new TargetPointer(expectedHeapList), info.HeapListAddress); @@ -597,7 +597,7 @@ public void GetCodeHeapInfo_LoaderCodeHeap(string version, MockTarget.Architectu var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - ICodeHeapInfo info = em.GetCodeHeapInfos().Single(); + ICodeHeapInfo info = em.GetCodeHeapInfos(JitManagerKind.EE).Single(); Assert.IsType(info); } @@ -613,7 +613,7 @@ public void GetCodeHeapInfo_HostCodeHeap(string version, MockTarget.Architecture var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - ICodeHeapInfo info = em.GetCodeHeapInfos().Single(); + ICodeHeapInfo info = em.GetCodeHeapInfos(JitManagerKind.EE).Single(); Assert.IsType(info); } @@ -627,7 +627,7 @@ public void GetCodeHeapInfo_LoaderCodeHeap_ReturnsLoaderHeapAddress(string versi var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - LoaderCodeHeapInfo loader = Assert.IsType(em.GetCodeHeapInfos().Single()); + LoaderCodeHeapInfo loader = Assert.IsType(em.GetCodeHeapInfos(JitManagerKind.EE).Single()); Target.TypeInfo loaderCodeHeapType = TargetTestHelpers.CreateTypeInfo(emBuilder.LoaderCodeHeapLayout); ulong loaderHeapFieldOffset = (ulong)loaderCodeHeapType.Fields[nameof(Data.LoaderCodeHeap.LoaderHeap)].Offset; Assert.Equal(new TargetPointer(heap.Address + loaderHeapFieldOffset), loader.LoaderHeapAddress); @@ -645,7 +645,7 @@ public void GetCodeHeapInfo_HostCodeHeap_ReturnsAddresses(string version, MockTa var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - HostCodeHeapInfo host = Assert.IsType(em.GetCodeHeapInfos().Single()); + HostCodeHeapInfo host = Assert.IsType(em.GetCodeHeapInfos(JitManagerKind.EE).Single()); Assert.Equal(expectedBase, host.BaseAddress); Assert.Equal(expectedCurrent, host.CurrentAddress); } @@ -675,7 +675,7 @@ public void GetCodeHeapList_SingleNode(string version, MockTarget.Architecture a var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); Assert.Single(heapInfos); Assert.IsType(heapInfos[0]); } @@ -721,7 +721,7 @@ public void GetCodeHeapList_LinkedList_TwoNodes(string version, MockTarget.Archi var target = CreateTarget(emBuilder); var em = target.Contracts.ExecutionManager; - List heapInfos = em.GetCodeHeapInfos().ToList(); + List heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList(); Assert.Equal(2, heapInfos.Count); // First heap (from node1) is a LoaderCodeHeap @@ -738,6 +738,69 @@ public void GetCodeHeapList_LinkedList_TwoNodes(string version, MockTarget.Archi Assert.Equal(currentAddr, hostInfo.CurrentAddress); } + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetJitManagerInfo_Interpreter(string version, MockTarget.Architecture arch) + { + MockExecutionManagerBuilder emBuilder = new(version, arch, MockExecutionManagerBuilder.DefaultAllocationRange); + const ulong HeapListAddress = 0x0012_3400; + emBuilder.SetInterpreterCodeHeaps(HeapListAddress); + + IExecutionManager executionManager = CreateTarget(emBuilder).Contracts.ExecutionManager; + JitManagerInfo info = Assert.IsType( + executionManager.GetJitManagerInfo(JitManagerKind.Interpreter)); + + Assert.Equal(new TargetPointer(emBuilder.InterpreterJitManagerAddress), info.ManagerAddress); + Assert.Equal(2u, info.CodeType); + Assert.Equal(new TargetPointer(HeapListAddress), info.HeapListAddress); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetJitManagerInfo_NoInterpreter(string version, MockTarget.Architecture arch) + { + IExecutionManager executionManager = CreateExecutionManagerContract(version, arch); + + Assert.Null(executionManager.GetJitManagerInfo(JitManagerKind.Interpreter)); + Assert.Empty(executionManager.GetCodeHeapInfos(JitManagerKind.Interpreter)); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetCodeHeapInfos_SelectsJitManager(string version, MockTarget.Architecture arch) + { + MockExecutionManagerBuilder emBuilder = new(version, arch, MockExecutionManagerBuilder.DefaultAllocationRange); + MockLoaderCodeHeap eeHeap = emBuilder.AddLoaderCodeHeap(); + MockHostCodeHeap interpreterHeap = emBuilder.AddHostCodeHeap(0x0005_0000, 0x0005_8000); + + MockCodeHeapListNode eeNode = emBuilder.AddCodeHeapListNode( + next: 0, + startAddress: 0x1000_0000, + endAddress: 0x1000_1000, + mapBase: 0, + headerMap: 0, + heap: eeHeap.Address); + MockCodeHeapListNode interpreterNode = emBuilder.AddCodeHeapListNode( + next: 0, + startAddress: 0x2000_0000, + endAddress: 0x2000_1000, + mapBase: 0, + headerMap: 0, + heap: interpreterHeap.Address); + emBuilder.SetAllCodeHeaps(eeNode.Address); + emBuilder.SetInterpreterCodeHeaps(interpreterNode.Address); + + IExecutionManager executionManager = CreateTarget(emBuilder).Contracts.ExecutionManager; + + LoaderCodeHeapInfo eeHeapInfo = Assert.IsType( + executionManager.GetCodeHeapInfos(JitManagerKind.EE).Single()); + HostCodeHeapInfo interpreterHeapInfo = Assert.IsType( + executionManager.GetCodeHeapInfos(JitManagerKind.Interpreter).Single()); + + Assert.Equal(eeHeap.Address, eeHeapInfo.HeapAddress.Value); + Assert.Equal(interpreterHeap.Address, interpreterHeapInfo.HeapAddress.Value); + } + [Theory] [MemberData(nameof(StdArchAllVersions))] public void GetStubKind_NoRangeSection(string version, MockTarget.Architecture arch) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.ExecutionManager.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.ExecutionManager.cs index 7f4a8ea5d389ad..c0009d5e9a1ef1 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.ExecutionManager.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.ExecutionManager.cs @@ -667,8 +667,9 @@ internal readonly struct JittedCodeRange internal Layout ImageDataDirectoryLayout { get; } internal Layout RuntimeFunctionLayout => _runtimeFunctions.RuntimeFunctionLayout; internal Layout UnwindInfoLayout => _runtimeFunctions.UnwindInfoLayout; - internal (string Name, ulong Value)[] Globals { get; } + internal (string Name, ulong Value)[] Globals { get; private set; } internal ulong EEJitManagerAddress { get; } + internal ulong InterpreterJitManagerAddress { get; private set; } internal ulong RangeSectionMapTopLevelAddress => _rangeSectionMapTopLevelAddress; private readonly MockRuntimeFunctionsBuilder _runtimeFunctions; @@ -743,6 +744,22 @@ internal MockExecutionManagerBuilder(string version, MockMemorySpace.Builder bui public void SetAllCodeHeaps(ulong headNodeAddress) => _eeJitManager.AllCodeHeaps = headNodeAddress; + public void SetInterpreterCodeHeaps(ulong headNodeAddress) + { + MockEEJitManager interpreterJitManager = AllocateAndCreate(EEJitManagerLayout, "InterpreterJitManager"); + interpreterJitManager.AllCodeHeaps = headNodeAddress; + InterpreterJitManagerAddress = interpreterJitManager.Address; + + ulong interpreterJitManagerGlobalAddress = AddPointerGlobal( + InterpreterJitManagerAddress, + "InterpreterJitManagerGlobalPointer"); + Globals = + [ + .. Globals, + (nameof(Constants.Globals.InterpreterJitManagerAddress), interpreterJitManagerGlobalAddress), + ]; + } + internal NibbleMapTestBuilderBase CreateNibbleMap(ulong codeRangeStart, uint codeRangeSize) { NibbleMapTestBuilderBase nibBuilder = Version switch