diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 103aac455a2a48..92c4516b8aae11 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -25,6 +25,13 @@ struct CodeBlockHandle TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle); // Get the instruction pointer address of the start of the funclet containing the code block TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle); + // Get the method region info (hot and cold code size, and cold code start address) + void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize); + // Get the JIT type + uint GetJITType(CodeBlockHandle codeInfoHandle); + // Attempt to get the method desc of an entrypoint + TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint); + // Gets the unwind info of the code block at the specified code pointer TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle); // Gets the base address the UnwindInfo of codeInfoHandle is relative to @@ -96,6 +103,9 @@ Data descriptors used: | `Bucket` | `Keys` | Array of keys of `HashMapSlotsPerBucket` length | | `Bucket` | `Values` | Array of values of `HashMapSlotsPerBucket` length | | `UnwindInfo` | `FunctionLength` | Length of the associated function in bytes. Only exists on some platforms | +| `UnwindInfo` | `CountOfUnwindCodes` | Number of unwind codes in the unwind info. Only exists on some platforms | +| `UnwindInfo` | `UnwindCodeOffset` | Offset of UnwindCodeOffset in the UnwindInfo data struct. Only exists on some platforms | +| `PortableEntryPoint` | `MethodDesc` | Method desc of portable entrypoint (only defined if `FeaturePortableEntrypoints` is enabled) | Global variables used: | Global Name | Type | Purpose | @@ -107,6 +117,7 @@ Global variables used: | `FeatureEHFunclets` | uint8 | 1 if EH funclets are enabled, 0 otherwise | | `GCInfoVersion` | uint32 | JITted code GCInfo version | | `FeatureOnStackReplacement` | uint8 | 1 if FEATURE_ON_STACK_REPLACEMENT is enabled, 0 otherwise | +| `FeaturePortableEntrypoints` | uint8 | 1 if FEATURE_PORTABLE_ENTRYPOINTS is enabled, 0 otherwise | Contracts used: | Contract Name | @@ -220,6 +231,142 @@ bool GetMethodInfo(TargetPointer rangeSection, TargetCodePointer jittedCodeAddre } ``` +The EE JitManager `GetMethodRegionInfo` sums up the lengths of each runtime function in a given method, attempting to find the runtime function length in several ways depending on the data availale on the platform. + +```csharp +public override void GetMethodRegionInfo(TargetPointer rangeSection, TargetCodePointer jittedCodeAddress, out uint hotSize, out TargetPointer coldStart, out uint coldSize) +{ + hotSize = 0; + coldStart = TargetPointer.Null; + coldSize = 0; + + TargetPointer start = // look up jittedCodeAddress in nibble map for rangeSection - see NibbleMap below + if (start == TargetPointer.Null) + return false; + + TargetNUInt relativeOffset = jittedCodeAddress - start; + int codeHeaderOffset = Target.PointerSize; + TargetPointer codeHeaderIndirect = start - codeHeaderOffset; + + // Check if address is in a stub code block + if (codeHeaderIndirect < Target.ReadGlobal("StubCodeBlockLast")) + return false; + + TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); + uint numUnwindInfos = Target.ReadPointer(codeHeaderAddress + /* RealCodeHeader::NumUnwindInfos offset */); + + if (numUnwindInfos == 0) + { + return; + } + TypeInfo type = target.GetTypeInfo(DataType.RuntimeFunction); + TypeInfo unwindType = target.GetTypeInfo(DataType.RuntimeFunction); + + // Sum up the lengths of all the runtime functions to get the hot size + for (uint i = 0; i < numUnwindInfos; i++) + { + TargetPointer addr = runtimeFunctions + (index * type.Size!.Value); + if (/* function has end address */) + hotSize += target.Read(addr + /* RuntimeFunction::EndAddress offset */) - target.Read(addr + /* RuntimeFunction::BeginAddress offset */) + + Data.UnwindInfo unwindInfo = _target.ProcessedData.GetOrAdd(function.UnwindData); + TargetPointer unwindAddr = target.Read(addr + /* RuntimeFunction::UnwindData offset */); + + if (/* unwind datatype has function length */) + hotSize += target.Read(unwindAddr + /* UnwindInfo::FunctionLength offset */) + + // First 18 bits are function length / (pointer size / 2). + // See UnwindFragmentInfo::Finalize + uint funcLengthInHeader = target.Read(unwindAddr + /* UnwindInfo::Header offset */) & ((1 << 18) - 1); + hotSize += (uint)(funcLengthInHeader * (_target.PointerSize / 2)); + } + return hotSize; +} +``` + +The R2R JitManager `GetMethodRegionInfo` has different behavior depending on whether the method was found in the hot cold map. If it was found, we find the bounds of the hot and cold regions by finding the start indices of the next hot and cold regions. If it is not in the map, we simply add the lengths of the runtime functions as shown above. +```csharp +public override void GetMethodRegionInfo(TargetPointer rangeSection, TargetCodePointer jittedCodeAddress, out uint hotSize, out TargetPointer coldStart, out uint coldSize) +{ + hotSize = 0; + coldSize = 0; + coldStart = TargetPointer.Null; + + info = default; + + TargetPointer r2rModule = Target.ReadPointer(/* range section address + RangeSection::R2RModule offset */); + TargetPointer r2rInfo = Target.ReadPointer(r2rModule + /* Module::ReadyToRunInfo offset */); + + // Check if address is in a thunk + if (/* jittedCodeAddress is in ReadyToRunInfo::DelayLoadMethodCallThunks */) + return false; + + // Find the relative address that we are looking for + TargetCodePointer addr = /* code pointer from jittedCodeAddress using PlatformMetadata.GetCodePointerFlags */ + TargetPointer imageBase = Target.ReadPointer(/* range section address + RangeSection::RangeBegin offset */); + TargetPointer relativeAddr = addr - imageBase; + + TargetPointer runtimeFunctions = Target.ReadPointer(r2rInfo + /* ReadyToRunInfo::RuntimeFunctions offset */); + int index = // Iterate through runtimeFunctions and find index of function with relativeAddress + + // look up hot and cold start and end indices in hot/cold map (hotIdx, coldStartIdx, coldEndIdx) + if (/* found in hot/cold map */) + { + hotSize = /* runtime function length at index hotIdx */ ; + // Sum up the lengths of all the runtime functions to get the cold size + for (uint i = coldStartIdx; i <= coldEndIdx; i++) + { + coldSize += // add up lengths of runtime functions at index i as shown above in the EEJitManager + } + coldStart = imageBase + /* start address of runtime function at index coldStartIdx */; + } + else + { + // No hot/cold splitting for this method, sum up the hot size only + uint hotStartIdx = // iterate through until you reach a valid MethodDesc signifying beginning of hot area + uint hotEndIdx = // iterate through until you reach a valid MethodDesc signifying beginning of next hot area + for (uint i = hotStartIdx; i <= hotEndIdx; i++) + { + hotSize += // add up lengths of runtime functions at index i as shown above in the EEJitManager + } + } +} + +``` + +`GetJitType` returns the JIT type by finding the JIT manager for the data range containing the relevant code block. We return TYPE_JIT for the EEJitManager, TYPE_R2R for the R2RJitManager, and TYPE_UNKNOWN for any other value. +```csharp +private enum JITTypes +{ + TYPE_UNKNOWN = 0, + TYPE_JIT = 1, + TYPE_R2R = 2, + TYPE_INTERPRETER = 3 +}; +``` +`NonVirtualEntry2MethodDesc` attempts to find a method desc from an entrypoint. If portable entrypoints are enabled, we attempt to read the entrypoint data structure to find the method table. We also attempt to find the method desc from a precode stub. Finally, we attempt to find the method desc using `GetMethodInfo` as described above. +```csharp +TargetPointer IExecutionManager.NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) +{ + TargetPointer rangeSection = // find range section corresponding to jittedCodeAddress - see RangeSectionMap + if (/* no corresponding range section */) + return null; + + if (/* range flags indicate RangeList */) + { + IPrecodeStubs precodeStubs = _target.Contracts.PrecodeStubs; + return precodeStubs.GetMethodDescFromPrecode(entrypoint); + } + else + { + // get the jit manager + // attempt to get the method info from a code block + } + return TargetPointer.Null; +} +``` + + The `CodeBlock` encapsulates the `MethodDesc` data from the target runtime together with the start of the jitted method ```csharp @@ -284,7 +431,7 @@ For R2R images, `hasFlagByte` is always `false`. * For jitted code (`EEJitManager`) a pointer to the `GCInfo` is stored on the `RealCodeHeader` which is accessed in the same way as `GetMethodInfo` described above. This can simply be returned as is. The `GCInfoVersion` is defined by the runtime global `GCInfoVersion`. -* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). The size of the unwind data is a platform specific. Currently only X86 is supported with a constant unwind data size of 32-bits. +* For R2R code (`ReadyToRunJitManager`), the `GCInfo` is stored directly after the `UnwindData`. This in turn is found by looking up the `UnwindInfo` (`RUNTIME_FUNCTION`) and reading the `UnwindData` offset. We find the `UnwindInfo` as described above in `IExecutionManager.GetUnwindInfo`. Once we have the relevant unwind data, we calculate the size of the unwind data and return a pointer to the following byte (first byte of the GCInfo). The size of the unwind data is a platform specific. See src/coreclr/vm/codeman.cpp GetUnwindDataBlob for more details. * The `GCInfoVersion` of R2R code is mapped from the R2R MajorVersion and MinorVersion which is read from the ReadyToRunHeader which itself is read from the ReadyToRunInfo (can be found as in GetMethodInfo). The current GCInfoVersion mapping is: * MajorVersion >= 11 and MajorVersion < 15 => 4 diff --git a/docs/design/datacontracts/PrecodeStubs.md b/docs/design/datacontracts/PrecodeStubs.md index b336b433ac2e13..556357b141b518 100644 --- a/docs/design/datacontracts/PrecodeStubs.md +++ b/docs/design/datacontracts/PrecodeStubs.md @@ -6,7 +6,7 @@ This contract provides support for examining [precode](../coreclr/botr/method-de ```csharp // Gets a pointer to the MethodDesc for a given stub entrypoint - TargetPointer GetMethodDescFromStubAddress(TargetCodePointer entryPoint); + TargetPointer GetMethodDescFromPrecode(TargetCodePointer entryPoint); ``` ## Version 1, 2, and 3 @@ -289,7 +289,7 @@ After the initial precode type is determined, for stub precodes a refined precod throw new InvalidOperationException($"Invalid precode type 0x{instrPointer:x16}"); } - TargetPointer IPrecodeStubs.GetMethodDescFromStubAddress(TargetCodePointer entryPoint) + TargetPointer IPrecodeStubs.GetMethodDescFromPrecode(TargetCodePointer entryPoint) { ValidPrecode precode = GetPrecodeFromEntryPoint(entryPoint); diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 507b3dbe852039..d088cda8fe7a76 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -1421,7 +1421,7 @@ Getting a MethodDesc for a certain slot in a MethodTable // Stub path, read address as a Precode and get the MethodDesc from it { - TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromStubAddress(pCode); + TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromPrecode(pCode); return methodDescPtr; } } diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index cd5b4b13768c8a..c02bfeb0818862 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -481,6 +481,13 @@ CDAC_TYPE_BEGIN(CodePointer) CDAC_TYPE_SIZE(sizeof(PCODE)) CDAC_TYPE_END(CodePointer) +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +CDAC_TYPE_BEGIN(PortableEntryPoint) +CDAC_TYPE_INDETERMINATE(PortableEntryPoint) +CDAC_TYPE_FIELD(PortableEntryPoint, /*pointer*/, MethodDesc, cdac_data::MethodDesc) +CDAC_TYPE_END(PortableEntryPoint) +#endif + CDAC_TYPE_BEGIN(MethodDescCodeData) CDAC_TYPE_INDETERMINATE(MethodDescCodeData) CDAC_TYPE_FIELD(MethodDescCodeData, /*CodePointer*/, TemporaryEntryPoint, offsetof(MethodDescCodeData,TemporaryEntryPoint)) @@ -585,12 +592,13 @@ CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTI CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, UnwindData, offsetof(RUNTIME_FUNCTION, UnwindData)) CDAC_TYPE_END(RuntimeFunction) +#if defined(TARGET_AMD64) || defined(TARGET_X86) CDAC_TYPE_BEGIN(UnwindInfo) -CDAC_TYPE_INDETERMINATE(UnwindInfo) #ifdef TARGET_X86 CDAC_TYPE_FIELD(UnwindInfo, /*uint32*/, FunctionLength, offsetof(UNWIND_INFO, FunctionLength)) #endif CDAC_TYPE_END(UnwindInfo) +#endif CDAC_TYPE_BEGIN(HashMap) CDAC_TYPE_INDETERMINATE(HashMap) @@ -965,6 +973,11 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2) #else CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) #endif //TARGET_64BIT +#ifdef FEATURE_PORTABLE_ENTRYPOINTS +CDAC_GLOBAL(FeaturePortableEntrypoints, uint8, 1) +#else +CDAC_GLOBAL(FeaturePortableEntrypoints, uint8, 0) +#endif // FEATURE_PORTABLE_ENTRYPOINTS CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A) CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET) diff --git a/src/coreclr/vm/precode_portable.hpp b/src/coreclr/vm/precode_portable.hpp index 57cf812e7186ae..0da166ace211b2 100644 --- a/src/coreclr/vm/precode_portable.hpp +++ b/src/coreclr/vm/precode_portable.hpp @@ -8,6 +8,7 @@ #ifndef FEATURE_PORTABLE_ENTRYPOINTS #error Requires FEATURE_PORTABLE_ENTRYPOINTS to be set #endif // !FEATURE_PORTABLE_ENTRYPOINTS +#include "cdacdata.h" class PortableEntryPoint final { @@ -62,7 +63,14 @@ class PortableEntryPoint final // pActualCode is a managed calling convention -> interpreter executor call stub in this case. return _pInterpreterData != nullptr && _pActualCode != nullptr; } + friend struct ::cdac_data; }; +template<> +struct cdac_data +{ + static constexpr size_t MethodDesc = offsetof(PortableEntryPoint, _pMD); +}; + extern InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; 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..52046befb5b0eb 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 @@ -19,10 +19,12 @@ public interface IExecutionManager : IContract TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize) => throw new NotImplementedException(); + uint GetJITType(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); + TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) => throw new NotImplementedException(); TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException(); TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => throw new NotImplementedException(); - // **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(); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IPrecodeStubs.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IPrecodeStubs.cs index aced1d2316dd08..cd024343186ca1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IPrecodeStubs.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IPrecodeStubs.cs @@ -8,7 +8,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; public interface IPrecodeStubs : IContract { static string IContract.Name { get; } = nameof(PrecodeStubs); - TargetPointer GetMethodDescFromStubAddress(TargetCodePointer entryPoint) => throw new NotImplementedException(); + TargetPointer GetMethodDescFromPrecode(TargetCodePointer entryPoint) => throw new NotImplementedException(); } public readonly struct PrecodeStubs : IPrecodeStubs diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 5dcb0078bf9854..31fba6b1c673cc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -104,6 +104,7 @@ public enum DataType HashMap, Bucket, UnwindInfo, + UnwindCode, NonVtableSlot, MethodImpl, NativeCodeSlot, @@ -114,6 +115,7 @@ public enum DataType InstMethodHashTable, EEJitManager, PatchpointInfo, + PortableEntryPoint, TransitionBlock, DebuggerEval, 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 084afe879f99c1..19887f0ead64bb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -16,6 +16,7 @@ public static class Globals public const string FeatureCOMInterop = nameof(FeatureCOMInterop); public const string FeatureOnStackReplacement = nameof(FeatureOnStackReplacement); + public const string FeaturePortableEntrypoints = nameof(FeaturePortableEntrypoints); public const string ObjectToMethodTableUnmask = nameof(ObjectToMethodTableUnmask); public const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs index c62499cad64ef7..e0234a7b5e7f05 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.EEJitManager.cs @@ -44,6 +44,37 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer return true; } + public override void GetMethodRegionInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out uint hotSize, out TargetPointer coldStart, out uint coldSize) + { + hotSize = 0; + coldStart = TargetPointer.Null; + coldSize = 0; + + if (rangeSection.IsRangeList) + return; + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress); + if (codeStart == TargetPointer.Null) + return; + Debug.Assert(codeStart.Value <= jittedCodeAddress.Value); + + if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader)) + return; + + if (realCodeHeader.NumUnwindInfos == 0) + { + return; + } + // Sum up the lengths of all the runtime functions to get the hot size + for (uint i = 0; i < realCodeHeader.NumUnwindInfos; i++) + { + Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(realCodeHeader.UnwindInfos, i); + hotSize += _runtimeFunctions.GetFunctionLength(function); + } + } + public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) { if (rangeSection.IsRangeList) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs index 166e85ca8945b3..67cf0994445255 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerCore.ReadyToRunJitManager.cs @@ -60,6 +60,43 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer return true; } + public override void GetMethodRegionInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out uint hotSize, out TargetPointer coldStart, out uint coldSize) + { + hotSize = 0; + coldSize = 0; + coldStart = TargetPointer.Null; + + Data.ReadyToRunInfo r2rInfo = GetReadyToRunInfo(rangeSection); + if (!GetRuntimeFunction(rangeSection, r2rInfo, jittedCodeAddress, out TargetPointer imageBase, out uint index)) + return; + + if (_hotCold.TryGetHotColdStartEnd(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index, r2rInfo.NumRuntimeFunctions, out uint hotIdx, out uint coldStartIdx, out uint coldEndIdx)) + { + + Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, hotIdx); + hotSize = _runtimeFunctions.GetFunctionLength(function); + // Sum up the lengths of all the runtime functions to get the cold size + for (uint i = coldStartIdx; i <= coldEndIdx; i++) + { + function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, i); + coldSize += _runtimeFunctions.GetFunctionLength(function); + } + coldStart = imageBase + _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, coldStartIdx).BeginAddress; + } + else + { + // No hot/cold splitting for this method, sum up the hot size only + uint hotStartIdx = AdjustRuntimeFunctionToMethodStart(r2rInfo, imageBase, index, out _); + uint hotEndIdx = AdjustRuntimeFunctionToMethodEnd(r2rInfo, imageBase, hotStartIdx); + + for (uint i = hotStartIdx; i <= hotEndIdx; i++) + { + Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, i); + hotSize += _runtimeFunctions.GetFunctionLength(function); + } + } + } + public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) { // ReadyToRunJitManager::JitCodeToMethodInfo @@ -120,7 +157,7 @@ public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jitt Data.RuntimeFunction runtimeFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index); TargetPointer unwindInfo = runtimeFunction.UnwindData + imageBase; - uint unwindDataSize = GetUnwindDataSize(); + uint unwindDataSize = UnwindDataSize.GetUnwindDataSize(Target, unwindInfo, imageBase, Target.Contracts.RuntimeInfo.GetTargetArchitecture()); gcInfo = unwindInfo + unwindDataSize; gcVersion = GetR2RGCInfoVersion(r2rInfo); } @@ -141,16 +178,6 @@ private uint GetR2RGCInfoVersion(Data.ReadyToRunInfo r2rInfo) }; } - private uint GetUnwindDataSize() - { - RuntimeInfoArchitecture arch = Target.Contracts.RuntimeInfo.GetTargetArchitecture(); - return arch switch - { - RuntimeInfoArchitecture.X86 => sizeof(uint), - _ => throw new NotSupportedException($"GetUnwindDataSize not supported for architecture: {arch}") - }; - } - #region RuntimeFunction Helpers private Data.ReadyToRunInfo GetReadyToRunInfo(RangeSection rangeSection) @@ -214,6 +241,25 @@ private uint AdjustRuntimeFunctionToMethodStart(Data.ReadyToRunInfo r2rInfo, Tar return index; } + private uint AdjustRuntimeFunctionToMethodEnd(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint index) + { + TargetPointer methodDesc; + do + { + // Funclets won't have a direct entry in the map of runtime function entry point to method desc. + // The funclet's address (and index) will be greater than that of the corresponding function, so + // we increment the index to find the actual function / method desc for the funclet. + index++; + if (index >= r2rInfo.NumRuntimeFunctions) + { + break; + } + methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index); + } while (methodDesc == TargetPointer.Null); + + return index - 1; + } + private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) { if (r2rInfo.DelayLoadMethodCallThunks == TargetPointer.Null) 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..278dceb500a80a 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 @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; @@ -55,6 +56,14 @@ private enum RangeSectionFlags : int RangeList = 0x04, } + private enum JitTypes + { + TYPE_UNKNOWN = 0, + TYPE_JIT = 1, + TYPE_R2R = 2, + TYPE_INTERPRETER = 3 + }; + private abstract class JitManager { public Target Target { get; } @@ -65,6 +74,7 @@ protected JitManager(Target target) } public abstract bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info); + public abstract void GetMethodRegionInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out uint hotSize, out TargetPointer coldStart, out uint coldSize); public abstract TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress); public abstract TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out bool hasFlagByte); public abstract void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion); @@ -184,10 +194,7 @@ TargetCodePointer IExecutionManager.GetStartAddress(CodeBlockHandle codeInfoHand TargetCodePointer IExecutionManager.GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) { - if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); - - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); if (range.Data == null) throw new InvalidOperationException("Unable to get runtime function address"); @@ -205,12 +212,71 @@ TargetCodePointer IExecutionManager.GetFuncletStartAddress(CodeBlockHandle codeI return range.Data.RangeBegin + runtimeFunction.BeginAddress; } - TargetPointer IExecutionManager.GetUnwindInfo(CodeBlockHandle codeInfoHandle) + void IExecutionManager.GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize) { - if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + hotSize = 0; + coldStart = TargetPointer.Null; + coldSize = 0; - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); + if (range.Data == null) + return; + + JitManager jitManager = GetJitManager(range.Data); + + jitManager.GetMethodRegionInfo(range, codeInfoHandle.Address.Value, out hotSize, out coldStart, out coldSize); + } + + uint IExecutionManager.GetJITType(CodeBlockHandle codeInfoHandle) + { + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); + if (range.Data == null) + return 0; + + JitManager jitManager = GetJitManager(range.Data); + + if (jitManager == _eeJitManager) + { + return (uint)JitTypes.TYPE_JIT; + } + else if (jitManager == _r2rJitManager) + { + return (uint)JitTypes.TYPE_R2R; + } + else + { + return (uint)JitTypes.TYPE_UNKNOWN; + } + } + + TargetPointer IExecutionManager.NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) + { + if (_target.ReadGlobal(Constants.Globals.FeaturePortableEntrypoints) != 0) + { + Data.PortableEntryPoint portableEntryPoint = _target.ProcessedData.GetOrAdd(entrypoint.AsTargetPointer); + return portableEntryPoint.MethodDesc; + } + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, entrypoint); + if (range.Data == null) + return TargetPointer.Null; + if (range.IsRangeList) + { + IPrecodeStubs precodeStubs = _target.Contracts.PrecodeStubs; + return precodeStubs.GetMethodDescFromPrecode(entrypoint); + } + else + { + JitManager jitManager = GetJitManager(range.Data); + if (jitManager.GetMethodInfo(range, entrypoint, out CodeBlock? info) && info != null) + { + return info.MethodDescAddress; + } + } + return TargetPointer.Null; + } + TargetPointer IExecutionManager.GetUnwindInfo(CodeBlockHandle codeInfoHandle) + { + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); if (range.Data == null) return TargetPointer.Null; @@ -221,10 +287,7 @@ TargetPointer IExecutionManager.GetUnwindInfo(CodeBlockHandle codeInfoHandle) TargetPointer IExecutionManager.GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) { - if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); - - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, new TargetCodePointer(codeInfoHandle.Address)); + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); if (range.Data == null) throw new InvalidOperationException($"{nameof(RangeSection)} not found for {codeInfoHandle.Address}"); @@ -234,10 +297,7 @@ TargetPointer IExecutionManager.GetUnwindInfoBaseAddress(CodeBlockHandle codeInf TargetPointer IExecutionManager.GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) { hasFlagByte = false; - if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); - - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); if (range.Data == null) return TargetPointer.Null; @@ -250,10 +310,7 @@ void IExecutionManager.GetGCInfo(CodeBlockHandle codeInfoHandle, out TargetPoint gcInfo = TargetPointer.Null; gcVersion = 0; - if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); - - RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + RangeSection range = RangeSectionFromCodeBlockHandle(codeInfoHandle); if (range.Data == null) return; @@ -269,4 +326,13 @@ TargetNUInt IExecutionManager.GetRelativeOffset(CodeBlockHandle codeInfoHandle) return info.RelativeOffset; } + + private RangeSection RangeSectionFromCodeBlockHandle(CodeBlockHandle codeInfoHandle) + { + if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + + RangeSection range = RangeSection.Find(_target, _topRangeSectionMap, _rangeSectionMapLookup, codeInfoHandle.Address.Value); + return range; + } } 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..5cc7d8cfae6539 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 @@ -18,6 +18,9 @@ internal ExecutionManager_1(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetMethodDesc(codeInfoHandle); public TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetStartAddress(codeInfoHandle); public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); + public void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize) => _executionManagerCore.GetMethodRegionInfo(codeInfoHandle, out hotSize, out coldStart, out coldSize); + public uint GetJITType(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetJITType(codeInfoHandle); + public TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) => _executionManagerCore.NonVirtualEntry2MethodDesc(entrypoint); public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); 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..8b20e02d27a390 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 @@ -18,6 +18,9 @@ internal ExecutionManager_2(Target target, Data.RangeSectionMap topRangeSectionM public TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetMethodDesc(codeInfoHandle); public TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetStartAddress(codeInfoHandle); public TargetCodePointer GetFuncletStartAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetFuncletStartAddress(codeInfoHandle); + public void GetMethodRegionInfo(CodeBlockHandle codeInfoHandle, out uint hotSize, out TargetPointer coldStart, out uint coldSize) => _executionManagerCore.GetMethodRegionInfo(codeInfoHandle, out hotSize, out coldStart, out coldSize); + public uint GetJITType(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetJITType(codeInfoHandle); + public TargetPointer NonVirtualEntry2MethodDesc(TargetCodePointer entrypoint) => _executionManagerCore.NonVirtualEntry2MethodDesc(entrypoint); public TargetPointer GetUnwindInfo(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfo(codeInfoHandle); public TargetPointer GetUnwindInfoBaseAddress(CodeBlockHandle codeInfoHandle) => _executionManagerCore.GetUnwindInfoBaseAddress(codeInfoHandle); public TargetPointer GetDebugInfo(CodeBlockHandle codeInfoHandle, out bool hasFlagByte) => _executionManagerCore.GetDebugInfo(codeInfoHandle, out hasFlagByte); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HotColdLookup.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HotColdLookup.cs index d2bea5f7eb3c2d..bddda521c8cef1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HotColdLookup.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HotColdLookup.cs @@ -17,6 +17,36 @@ private HotColdLookup(Target target) _target = target; } + public bool TryGetHotColdStartEnd( + uint numHotColdMap, + TargetPointer hotColdMap, + uint runtimeFunctionIndex, + uint numRuntimeFunctions, + out uint hotIdx, + out uint coldStart, + out uint coldEnd) + { + hotIdx = 0; + coldStart = 0; + coldEnd = 0; + + if (numHotColdMap == 0) + return false; + // The first entry in the HotColdMap is the runtime function index of the first cold part. + + if (TryLookupHotColdMappingForMethod(numHotColdMap, hotColdMap, runtimeFunctionIndex, out uint hotIndex, out uint coldIndex)) + { + hotIdx = _target.Read(hotColdMap + (ulong)hotIndex * sizeof(uint)); + + coldStart = _target.Read(hotColdMap + (ulong)coldIndex * sizeof(uint)); + coldEnd = (coldIndex + 2) < numHotColdMap + ? _target.Read(hotColdMap + (ulong)(coldIndex + 2) * sizeof(uint)) - 1 + : numRuntimeFunctions - 1; + return true; + } + return false; + } + public uint GetHotFunctionIndex(uint numHotColdMap, TargetPointer hotColdMap, uint runtimeFunctionIndex) { if (!IsColdCode(numHotColdMap, hotColdMap, runtimeFunctionIndex)) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs new file mode 100644 index 00000000000000..4f2a6d5e4744b0 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/UnwindDataSize.cs @@ -0,0 +1,95 @@ +// 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.Diagnostics; +using Microsoft.Diagnostics.DataContractReader.Contracts; + +namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; + +internal static class UnwindDataSize +{ + public static uint GetUnwindDataSize(Target target, TargetPointer unwindInfo, TargetPointer moduleBase, RuntimeInfoArchitecture arch) + { + switch (arch) + { + case RuntimeInfoArchitecture.X86: + return sizeof(uint); + case RuntimeInfoArchitecture.X64: + // see https://learn.microsoft.com/cpp/build/exception-handling-x64 + uint sizeOfUnwindCode = 2; // from spec + uint unwindCodeOffset = 4; // from spec + Data.UnwindInfo unwind = target.ProcessedData.GetOrAdd(unwindInfo); + return AlignUp((int)(sizeof(uint) + + (unwind.CountOfUnwindCodes * sizeOfUnwindCode) + + unwindCodeOffset), sizeof(uint)); + case RuntimeInfoArchitecture.Arm: + case RuntimeInfoArchitecture.Arm64: + TargetPointer xdata = unwindInfo; + uint xdata0 = target.Read(xdata); + uint size = 4; // initial header + uint unwindWords; + uint epilogScopes; + if (arch == RuntimeInfoArchitecture.Arm) + { + // See https://learn.microsoft.com/cpp/build/arm-exception-handling + unwindWords = xdata0 >> 28; + epilogScopes = (xdata0 >> 23) & 0x1F; + } + else + { + // See https://learn.microsoft.com/cpp/build/arm64-exception-handling + unwindWords = xdata0 >> 27; + epilogScopes = (xdata0 >> 22) & 0x1F; + } + if (unwindWords == 0 && epilogScopes == 0) + { + size += 4; + uint xdata1 = target.Read(xdata + 4); + unwindWords = (xdata1 >> 16) & 0xff; + epilogScopes = xdata1 & 0xffff; + } + + if ((xdata0 & (1 << 21)) != 0) + size += 4 * epilogScopes; + + size += 4 * unwindWords; + size += 4; + return size; + + case RuntimeInfoArchitecture.LoongArch64: + case RuntimeInfoArchitecture.RiscV64: + xdata = unwindInfo; + xdata0 = target.Read(xdata); + + // If both Epilog Count and Code Word is not zero + // Info of Epilog and Unwind scopes are given by 1 word header + // Otherwise this info is given by a 2 word header + if ((xdata0 >> 27) != 0) + { + size = 4; + epilogScopes = (xdata0 >> 22) & 0x1f; + unwindWords = (xdata0 >> 27) & 0x1f; + } + else + { + size = 8; + uint xdata1 = target.Read(xdata + 4); + epilogScopes = xdata1 & 0xffff; + unwindWords = (xdata1 >> 16) & 0xff; + } + + if ((xdata0 & (1 << 21)) != 0) + size += 4 * epilogScopes; + + size += 4 * unwindWords; + + size += 4; // exception handler RVA + return size; + default: + throw new NotSupportedException($"GetUnwindDataSize not supported for architecture: {arch}"); + } + } + private static uint AlignUp(int offset, int align) + => (uint)((offset + align - 1) & ~(align - 1)); +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs index 88c61ef14cb742..75bfae4242e3d6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs @@ -138,7 +138,7 @@ public PrecodeStubsCommon(Target target, Data.PrecodeMachineDescriptor precodeMa _codePointerFlags = codePointerFlags; } - TargetPointer IPrecodeStubs.GetMethodDescFromStubAddress(TargetCodePointer entryPoint) + TargetPointer IPrecodeStubs.GetMethodDescFromPrecode(TargetCodePointer entryPoint) { ValidPrecode precode = GetPrecodeFromEntryPoint(entryPoint); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 5b79274a88a7e0..7600a50a8332b9 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1177,7 +1177,7 @@ private readonly TargetPointer GetMethodDescForEntrypoint(TargetCodePointer pCod // stub path, read address as a Precode and read MethodDesc from it { - TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromStubAddress(pCode); + TargetPointer methodDescPtr = _target.Contracts.PrecodeStubs.GetMethodDescFromPrecode(pCode); return methodDescPtr; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PortableEntryPoint.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PortableEntryPoint.cs new file mode 100644 index 00000000000000..4e5ac6869f32d8 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PortableEntryPoint.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class PortableEntryPoint : IData +{ + static PortableEntryPoint IData.Create(Target target, TargetPointer address) => new PortableEntryPoint(target, address); + public PortableEntryPoint(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.PortableEntryPoint); + + MethodDesc = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodDesc)].Offset); + } + public TargetPointer MethodDesc { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/UnwindInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/UnwindInfo.cs index add5603fdfd517..d5bcee08f281ed 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/UnwindInfo.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/UnwindInfo.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Diagnostics.DataContractReader.Contracts; + namespace Microsoft.Diagnostics.DataContractReader.Data; internal sealed class UnwindInfo : IData @@ -22,8 +24,15 @@ public UnwindInfo(Target target, TargetPointer address) // Otherwise, it starts with a bitfield header Header = target.Read(address); } - } + RuntimeInfoArchitecture arch = target.Contracts.RuntimeInfo.GetTargetArchitecture(); + if (arch == RuntimeInfoArchitecture.X64) + { + // see https://learn.microsoft.com/cpp/build/exception-handling-x64 + CountOfUnwindCodes = target.Read(address + 2); + } + } public uint? FunctionLength { get; } public uint? Header { get; } + public byte CountOfUnwindCodes { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs index 74f548675b832b..209ef4ba36ee4b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs @@ -196,7 +196,7 @@ internal bool ValidateMethodDescPointer(TargetPointer methodDescPointer, [NotNul if (temporaryEntryPoint != TargetCodePointer.Null) { Contracts.IPrecodeStubs precode = _target.Contracts.PrecodeStubs; - TargetPointer methodDesc = precode.GetMethodDescFromStubAddress(temporaryEntryPoint); + TargetPointer methodDesc = precode.GetMethodDescFromPrecode(temporaryEntryPoint); if (methodDesc != methodDescPointer) { return false; diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 60cf2ab13a5b29..5a36e3d06aec6a 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -349,6 +349,26 @@ internal unsafe partial interface ISOSEnum int GetCount(uint* pCount); } +internal enum JitTypes +{ + TYPE_UNKNOWN = 0, + TYPE_JIT, + TYPE_PJIT, + TYPE_INTERPRETER +} + +internal struct DacpCodeHeaderData +{ + public ClrDataAddress GCInfo; + public JitTypes JITType; + public ClrDataAddress MethodDescPtr; + public ClrDataAddress MethodStart; + public uint MethodSize; + public ClrDataAddress ColdRegionStart; + public uint ColdRegionSize; + public uint HotRegionSize; +} + [GeneratedComInterface] [Guid("436f00f2-b42a-4b9f-870c-e73db66ae930")] internal unsafe partial interface ISOSDacInterface @@ -416,7 +436,7 @@ internal unsafe partial interface ISOSDacInterface // JIT Data [PreserveSig] - int GetCodeHeaderData(ClrDataAddress ip, /*struct DacpCodeHeaderData*/ void* data); + int GetCodeHeaderData(ClrDataAddress ip, DacpCodeHeaderData* data); [PreserveSig] int GetJitManagerList(uint count, /*struct DacpJitManagerInfo*/ void* managers, uint* pNeeded); [PreserveSig] diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 821394b257cab1..4675cd67d8d866 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -607,7 +607,6 @@ int ISOSDacInterface.GetClrWatsonBuckets(ClrDataAddress thread, void* pGenericMo { hr = ex.HResult; } - #if DEBUG if (_legacyImpl is not null) { @@ -628,8 +627,66 @@ int ISOSDacInterface.GetClrWatsonBuckets(ClrDataAddress thread, void* pGenericMo #endif return hr; } - int ISOSDacInterface.GetCodeHeaderData(ClrDataAddress ip, void* data) - => _legacyImpl is not null ? _legacyImpl.GetCodeHeaderData(ip, data) : HResults.E_NOTIMPL; + int ISOSDacInterface.GetCodeHeaderData(ClrDataAddress ip, DacpCodeHeaderData* data) + { + int hr = HResults.S_OK; + try + { + if (ip == 0 || data == null) + throw new ArgumentException(); + + IExecutionManager executionManager = _target.Contracts.ExecutionManager; + TargetCodePointer targetCodePointer = ip.ToTargetCodePointer(_target); + CodeBlockHandle? codeBlockHandle = executionManager.GetCodeBlockHandle(targetCodePointer); + if (codeBlockHandle == null) + { + TargetPointer methodDesc = executionManager.NonVirtualEntry2MethodDesc(targetCodePointer); + if (methodDesc == TargetPointer.Null) + throw new ArgumentException(); + data->MethodDescPtr = methodDesc.ToClrDataAddress(_target); + data->JITType = JitTypes.TYPE_UNKNOWN; + data->GCInfo = 0; + data->MethodStart = 0; + data->MethodSize = 0; + data->ColdRegionStart = 0; + } + else + { + data->MethodDescPtr = executionManager.GetMethodDesc(codeBlockHandle.Value).ToClrDataAddress(_target); + data->JITType = (JitTypes)executionManager.GetJITType(codeBlockHandle.Value); + executionManager.GetGCInfo(codeBlockHandle.Value, out TargetPointer gcInfo, out uint gcVersion); + data->GCInfo = gcInfo.ToClrDataAddress(_target); + data->MethodStart = executionManager.GetStartAddress(codeBlockHandle.Value).Value; + executionManager.GetMethodRegionInfo(codeBlockHandle.Value, out data->HotRegionSize, out TargetPointer coldRegionStart, out data->ColdRegionSize); + data->ColdRegionStart = coldRegionStart.ToClrDataAddress(_target); + data->MethodSize = data->HotRegionSize + data->ColdRegionSize; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl is not null) + { + DacpCodeHeaderData dataLocal = default; + int hrLocal = _legacyImpl.GetCodeHeaderData(ip, &dataLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + Debug.Assert(data->MethodDescPtr == dataLocal.MethodDescPtr, $"cDAC: {data->MethodDescPtr:x}, DAC: {dataLocal.MethodDescPtr:x}"); + Debug.Assert(data->JITType == dataLocal.JITType, $"cDAC: {data->JITType}, DAC: {dataLocal.JITType}"); + Debug.Assert(data->GCInfo == dataLocal.GCInfo, $"cDAC: {data->GCInfo:x}, DAC: {dataLocal.GCInfo:x}"); + Debug.Assert(data->MethodStart == dataLocal.MethodStart, $"cDAC: {data->MethodStart:x}, DAC: {dataLocal.MethodStart:x}"); + Debug.Assert(data->MethodSize == dataLocal.MethodSize, $"cDAC: {data->MethodSize}, DAC: {dataLocal.MethodSize}"); + Debug.Assert(data->HotRegionSize == dataLocal.HotRegionSize, $"cDAC: {data->HotRegionSize}, DAC: {dataLocal.HotRegionSize}"); + Debug.Assert(data->ColdRegionStart == dataLocal.ColdRegionStart, $"cDAC: {data->ColdRegionStart:x}, DAC: {dataLocal.ColdRegionStart:x}"); + Debug.Assert(data->ColdRegionSize == dataLocal.ColdRegionSize, $"cDAC: {data->ColdRegionSize}, DAC: {dataLocal.ColdRegionSize}"); + } + } +#endif + return hr; + } int ISOSDacInterface.GetCodeHeapList(ClrDataAddress jitManager, uint count, void* codeHeaps, uint* pNeeded) => _legacyImpl is not null ? _legacyImpl.GetCodeHeapList(jitManager, count, codeHeaps, pNeeded) : HResults.E_NOTIMPL; int ISOSDacInterface.GetDacModuleHandle(void* phModule) diff --git a/src/native/managed/cdac/tests/PrecodeStubsTests.cs b/src/native/managed/cdac/tests/PrecodeStubsTests.cs index cbab72d9e3d729..231798ee70409e 100644 --- a/src/native/managed/cdac/tests/PrecodeStubsTests.cs +++ b/src/native/managed/cdac/tests/PrecodeStubsTests.cs @@ -387,13 +387,13 @@ public void TestPrecodeStubPrecodeExpectedMethodDesc(PrecodeTestDescriptor test, Assert.NotNull(precodeContract); - var actualMethodDesc = precodeContract.GetMethodDescFromStubAddress(stub1); + var actualMethodDesc = precodeContract.GetMethodDescFromPrecode(stub1); Assert.Equal(expectedMethodDesc, actualMethodDesc); if (contractVersion >= 2) { // Implementation of this type of precode is only handled correctly in contract version 2 and higher - var actualMethodDesc2 = precodeContract.GetMethodDescFromStubAddress(stub2); + var actualMethodDesc2 = precodeContract.GetMethodDescFromPrecode(stub2); Assert.Equal(expectedMethodDesc2, actualMethodDesc2); } }