Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions docs/design/datacontracts/ExecutionManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICodeHeapInfo> 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<ICodeHeapInfo> GetCodeHeapInfos(JitManagerKind kind);

// Get the exception clause info for the code block
List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle);
Expand Down Expand Up @@ -74,6 +73,12 @@ public struct JitManagerInfo
public uint CodeType;
public TargetPointer HeapListAddress;
}

public enum JitManagerKind
{
EE,
Interpreter,
}
```

```csharp
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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 */),
};
}

Expand All @@ -603,10 +632,13 @@ private ICodeHeapInfo GetCodeHeapInfo(TargetPointer codeHeapAddress)
};
}

IEnumerable<ICodeHeapInfo> IExecutionManager.GetCodeHeapInfos()
IEnumerable<ICodeHeapInfo> 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 */);
Expand Down
3 changes: 2 additions & 1 deletion docs/design/datacontracts/RuntimeInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
3 changes: 2 additions & 1 deletion docs/design/datacontracts/data-descriptor-meanings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)",
Expand Down
28 changes: 28 additions & 0 deletions docs/design/datacontracts/reader-version-history.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions src/coreclr/vm/codeman.h
Original file line number Diff line number Diff line change
Expand Up @@ -2802,6 +2802,9 @@ struct cdac_data<ExecutionManager>
{
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
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -1827,6 +1827,9 @@ CDAC_GLOBAL(StressLogEnabled, T_UINT8, 0)
#endif
CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data<ExecutionManager>::CodeRangeMapAddress)
CDAC_GLOBAL_POINTER(EEJitManagerAddress, cdac_data<ExecutionManager>::EEJitManagerAddress)
#ifdef FEATURE_INTERPRETER
CDAC_GLOBAL_POINTER(InterpreterJitManagerAddress, cdac_data<ExecutionManager>::InterpreterJitManagerAddress)
#endif // FEATURE_INTERPRETER
#ifdef TARGET_WASM
CDAC_GLOBAL_POINTER(FunctionTableIndexRangeList, cdac_data<ExecutionManager>::FunctionTableIndexRangeListAddress)
#endif // TARGET_WASM
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public struct JitManagerInfo
public TargetPointer HeapListAddress;
}

public enum JitManagerKind
{
EE,
Interpreter,
}

public enum CodeKind : uint
{
Unknown = 0,
Expand Down Expand Up @@ -114,8 +120,8 @@ public interface IExecutionManager : IContract
bool IsGcSafe(TargetCodePointer instructionPointer) => throw new NotImplementedException();
List<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
uint GetStackParameterSize(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
JitManagerInfo GetEEJitManagerInfo() => throw new NotImplementedException();
IEnumerable<ICodeHeapInfo> GetCodeHeapInfos() => throw new NotImplementedException();
JitManagerInfo? GetJitManagerInfo(JitManagerKind kind) => throw new NotImplementedException();
IEnumerable<ICodeHeapInfo> GetCodeHeapInfos(JitManagerKind kind) => throw new NotImplementedException();
Comment thread
max-charlamb marked this conversation as resolved.
IReadOnlyList<TargetPointer> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Data.EEJitManager>(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<Data.EEJitManager>(jitManagerAddress);
return new JitManagerInfo
{
ManagerAddress = eeJitManagerAddr,
CodeType = 0, // miManaged | miIL
ManagerAddress = jitManagerAddress,
CodeType = codeType,
HeapListAddress = jitManager.AllCodeHeaps,
};
}
Expand All @@ -486,10 +512,15 @@ private ICodeHeapInfo GetCodeHeapInfo(TargetPointer codeHeapAddress)
};
}

IEnumerable<ICodeHeapInfo> IExecutionManager.GetCodeHeapInfos()
IEnumerable<ICodeHeapInfo> 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<Data.CodeHeapListNode>(nodeAddr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExceptionClauseInfo> GetExceptionClauses(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetExceptionClauses(codeInfoHandle);
public JitManagerInfo GetEEJitManagerInfo() => _executionManagerCore.GetEEJitManagerInfo();
public IEnumerable<ICodeHeapInfo> GetCodeHeapInfos() => _executionManagerCore.GetCodeHeapInfos();
public JitManagerInfo? GetJitManagerInfo(JitManagerKind kind) => _executionManagerCore.GetJitManagerInfo(kind);
public IEnumerable<ICodeHeapInfo> GetCodeHeapInfos(JitManagerKind kind) => _executionManagerCore.GetCodeHeapInfos(kind);
public IReadOnlyList<TargetPointer> GetDynamicFunctionTableEntries(TargetPointer tableAddress) => _executionManagerCore.GetDynamicFunctionTableEntries(tableAddress);
public CodeKind GetCodeKind(TargetCodePointer codeAddress) => _executionManagerCore.GetCodeKind(codeAddress);
public TargetPointer FindReadyToRunModule(TargetPointer address) => _executionManagerCore.FindReadyToRunModule(address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICodeHeapInfo> heapInfos = em.GetCodeHeapInfos().ToList();
List<ICodeHeapInfo> heapInfos = em.GetCodeHeapInfos(JitManagerKind.EE).ToList();
int i = 0;
if (codeHeaps is not null)
{
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading