From d33dae7220ed2da532179d2c4c534fd29313411a Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 11:25:56 -0800 Subject: [PATCH 01/21] Move CodePointerFromAddress to shared helper --- .../CodePointerUtils.cs | 27 +++++++++++++++++++ .../Contracts/RuntimeTypeSystem_1.cs | 17 +----------- .../MethodValidation.cs | 18 +------------ 3 files changed, 29 insertions(+), 33 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs new file mode 100644 index 0000000000000..a6e1763471572 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs @@ -0,0 +1,27 @@ +// 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; + +internal static class CodePointerUtils +{ + internal static TargetCodePointer CodePointerFromAddress(TargetPointer address, Target target) + { + IPlatformMetadata metadata = target.Contracts.PlatformMetadata; + CodePointerFlags flags = metadata.GetCodePointerFlags(); + if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit)) + { + return new TargetCodePointer(address.Value | 1); + } + else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth)) + { + throw new NotImplementedException($"{nameof(CodePointerFromAddress)}: ARM64 with pointer authentication"); + } + Debug.Assert(flags == default); + return new TargetCodePointer(address.Value); + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index d72c1b51a5af1..d13c382e30d65 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -1023,21 +1023,6 @@ private TargetPointer GetAddressOfNonVtableSlot(TargetPointer methodDescPointer, return methodDescPointer.Value + offset; } - private TargetCodePointer CodePointerFromAddress(TargetPointer address) - { - IPlatformMetadata metadata = _target.Contracts.PlatformMetadata; - CodePointerFlags flags = metadata.GetCodePointerFlags(); - if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit)) - { - return new TargetCodePointer(address.Value | 1); - } else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth)) - { - throw new NotImplementedException("CodePointerFromAddress: ARM64 with pointer authentication"); - } - Debug.Assert(flags == default); - return new TargetCodePointer(address.Value); - } - TargetCodePointer IRuntimeTypeSystem.GetNativeCode(MethodDescHandle methodDescHandle) { MethodDesc md = _methodDescs[methodDescHandle.Address]; @@ -1049,7 +1034,7 @@ TargetCodePointer IRuntimeTypeSystem.GetNativeCode(MethodDescHandle methodDescHa // This means that *ppCode is not stable. It can turn from non-zero to zero. TargetPointer ppCode = ((IRuntimeTypeSystem)this).GetAddressOfNativeCodeSlot(methodDescHandle); TargetCodePointer pCode = _target.ReadCodePointer(ppCode); - return CodePointerFromAddress(pCode.AsTargetPointer);; + return CodePointerUtils.CodePointerFromAddress(pCode.AsTargetPointer, _target); } if (!md.HasStableEntryPoint || md.HasPrecode) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs index 3471b7ffba8d5..b167270158feb 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodValidation.cs @@ -167,22 +167,6 @@ private TargetPointer GetAddressOfNonVtableSlot(TargetPointer methodDescPointer, return methodDescPointer.Value + offset; } - // FIXME[cdac]: this is copypasted from RuntimeTypeSystem_1 - put it in a a shared class instead - private TargetCodePointer CodePointerFromAddress(TargetPointer address) - { - IPlatformMetadata metadata = _target.Contracts.PlatformMetadata; - CodePointerFlags flags = metadata.GetCodePointerFlags(); - if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit)) - { - return new TargetCodePointer(address.Value | 1); - } else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth)) - { - throw new NotImplementedException("CodePointerFromAddress: ARM64 with pointer authentication"); - } - Debug.Assert(flags == default); - return new TargetCodePointer(address.Value); - } - private TargetCodePointer GetCodePointer(TargetPointer methodDescPointer, NonValidatedMethodDesc umd) { // TODO(cdac): _ASSERTE(!IsDefaultInterfaceMethod() || HasNativeCodeSlot()); @@ -194,7 +178,7 @@ private TargetCodePointer GetCodePointer(TargetPointer methodDescPointer, NonVal TargetPointer ppCode = GetAddrOfNativeCodeSlot(methodDescPointer, umd); TargetCodePointer pCode = _target.ReadCodePointer(ppCode); - return CodePointerFromAddress(pCode.AsTargetPointer); + return CodePointerUtils.CodePointerFromAddress(pCode.AsTargetPointer, _target); } if (!umd.HasStableEntryPoint || umd.HasPrecode) From 5f9d138ae05a3807ab78cdf1c25e38d613182f97 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 11:34:19 -0800 Subject: [PATCH 02/21] Add ReadyToRunInfo to data descriptor --- .../debug/runtimeinfo/datadescriptor.h | 10 ++++++ src/coreclr/vm/ceeload.h | 1 + src/coreclr/vm/readytoruninfo.h | 18 ++++++++-- .../DataType.cs | 3 +- .../Data/Module.cs | 2 ++ .../Data/ReadyToRunInfo.cs | 36 +++++++++++++++++++ 6 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 1bc05369a3871..2d9a22cbfd576 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -227,6 +227,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, ThunkHeap, cdac_data::ThunkHeap) CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data::DynamicMetadata) CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data::FileName) +CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data::ReadyToRunInfo) CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data::FieldDefToDescMap) CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data::ManifestModuleReferencesMap) @@ -412,6 +413,15 @@ CDAC_TYPE_INDETERMINATE(FixupPrecodeData) CDAC_TYPE_FIELD(FixupPrecodeData, /*pointer*/, MethodDesc, offsetof(FixupPrecodeData, MethodDesc)) CDAC_TYPE_END(FixupPrecodeData) +CDAC_TYPE_BEGIN(ReadyToRunInfo) +CDAC_TYPE_INDETERMINATE(ReadyToRunInfo) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data::CompositeInfo) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data::NumRuntimeFunctions) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data::RuntimeFunctions) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_data::DelayLoadMethodCallThunks) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) +CDAC_TYPE_END(ReadyToRunInfo) + CDAC_TYPE_BEGIN(RangeSectionMap) CDAC_TYPE_INDETERMINATE(RangeSectionMap) CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index e712365bbbdf7..66ef93ee8988f 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -1649,6 +1649,7 @@ struct cdac_data static constexpr size_t DynamicMetadata = offsetof(Module, m_pDynamicMetadata); static constexpr size_t Path = offsetof(Module, m_path); static constexpr size_t FileName = offsetof(Module, m_fileName); + static constexpr size_t ReadyToRunInfo = offsetof(Module, m_pReadyToRunInfo); // Lookup map pointers static constexpr size_t FieldDefToDescMap = offsetof(Module, m_FieldDefToDescMap); diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index a58bbb41b9009..9a3564798fc6c 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -28,7 +28,7 @@ class ReadyToRunCoreInfo PTR_PEImageLayout m_pLayout; PTR_READYTORUN_CORE_HEADER m_pCoreHeader; Volatile m_fForbidLoadILBodyFixups; - + public: ReadyToRunCoreInfo(); ReadyToRunCoreInfo(PEImageLayout * pLayout, READYTORUN_CORE_HEADER * pCoreHeader); @@ -123,7 +123,7 @@ class ReadyToRunInfo PTR_READYTORUN_IMPORT_SECTION m_pImportSections; DWORD m_nImportSections; - bool m_readyToRunCodeDisabled; // Is + bool m_readyToRunCodeDisabled; // Is NativeFormat::NativeReader m_nativeReader; NativeFormat::NativeArray m_methodDefEntryPoints; @@ -335,8 +335,20 @@ class ReadyToRunInfo PTR_MethodDesc GetMethodDescForEntryPointInNativeImage(PCODE entryPoint); void SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc); - + PTR_ReadyToRunCoreInfo GetComponentInfo() { return dac_cast(&m_component); } + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t CompositeInfo = offsetof(ReadyToRunInfo, m_pCompositeInfo); + static constexpr size_t NumRuntimeFunctions = offsetof(ReadyToRunInfo, m_nRuntimeFunctions); + static constexpr size_t RuntimeFunctions = offsetof(ReadyToRunInfo, m_pRuntimeFunctions); + static constexpr size_t DelayLoadMethodCallThunks = offsetof(ReadyToRunInfo, m_pSectionDelayLoadMethodCallThunks); + static constexpr size_t EntryPointToMethodDescMap = offsetof(ReadyToRunInfo, m_entryPointToMethodDescMap); }; class DynamicHelpers diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 60ee363c81d25..f1f4a4d97d60f 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -66,5 +66,6 @@ public enum DataType ILCodeVersioningState, NativeCodeVersionNode, ProfControlBlock, - ILCodeVersionNode + ILCodeVersionNode, + ReadyToRunInfo, } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs index ec19a12f10380..1341f0e73de99 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs @@ -25,6 +25,7 @@ public Module(Target target, TargetPointer address) DynamicMetadata = target.ReadPointer(address + (ulong)type.Fields[nameof(DynamicMetadata)].Offset); Path = target.ReadPointer(address + (ulong)type.Fields[nameof(Path)].Offset); FileName = target.ReadPointer(address + (ulong)type.Fields[nameof(FileName)].Offset); + ReadyToRunInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(ReadyToRunInfo)].Offset); FieldDefToDescMap = address + (ulong)type.Fields[nameof(FieldDefToDescMap)].Offset; ManifestModuleReferencesMap = address + (ulong)type.Fields[nameof(ManifestModuleReferencesMap)].Offset; @@ -43,6 +44,7 @@ public Module(Target target, TargetPointer address) public TargetPointer DynamicMetadata { get; init; } public TargetPointer Path { get; init; } public TargetPointer FileName { get; init; } + public TargetPointer ReadyToRunInfo { get; init; } public TargetPointer FieldDefToDescMap { get; init; } public TargetPointer ManifestModuleReferencesMap { get; init; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs new file mode 100644 index 0000000000000..2b07deae418f9 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ReadyToRunInfo : IData +{ + static ReadyToRunInfo IData.Create(Target target, TargetPointer address) + => new ReadyToRunInfo(target, address); + + private readonly Target _target; + + public ReadyToRunInfo(Target target, TargetPointer address) + { + _target = target; + Target.TypeInfo type = target.GetTypeInfo(DataType.ReadyToRunInfo); + + CompositeInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(CompositeInfo)].Offset); + + NumRuntimeFunctions = target.Read(address + (ulong)type.Fields[nameof(NumRuntimeFunctions)].Offset); + RuntimeFunctions = target.ReadPointer(address + (ulong)type.Fields[nameof(RuntimeFunctions)].Offset); + + DelayLoadMethodCallThunks = target.ReadPointer(address + (ulong)type.Fields[nameof(DelayLoadMethodCallThunks)].Offset); + + // Map is from the composite info pointer (set to itself for non-multi-assembly composite images) + EntryPointToMethodDescMap = CompositeInfo + (ulong)type.Fields[nameof(EntryPointToMethodDescMap)].Offset; + } + + private TargetPointer CompositeInfo { get; } + + public uint NumRuntimeFunctions { get; } + public TargetPointer RuntimeFunctions { get; } + + public TargetPointer DelayLoadMethodCallThunks { get; } + public TargetPointer EntryPointToMethodDescMap { get; } +} From b5609449adb9148e7852fad0540e0e4dd1f36661 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 11:59:34 -0800 Subject: [PATCH 03/21] ReadyToRunJitManager: check if address is in a thunk for READYTORUN_HELPER_DelayLoad_MethodCall --- .../debug/runtimeinfo/datadescriptor.h | 6 +++ .../DataType.cs | 1 + .../ExecutionManager/ExecutionManagerBase.cs | 13 +---- ...ExecutionManager_1.ReadyToRunJitManager.cs | 50 +++++++++++++++++++ .../Data/ImageDataDirectory.cs | 21 ++++++++ 5 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ImageDataDirectory.cs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 2d9a22cbfd576..b25633bfe7ed4 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -422,6 +422,12 @@ CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_dat CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) CDAC_TYPE_END(ReadyToRunInfo) +CDAC_TYPE_BEGIN(ImageDataDirectory) +CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY)) +CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress)) +CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, Size, offsetof(IMAGE_DATA_DIRECTORY, Size)) +CDAC_TYPE_END(ImageDataDirectory) + CDAC_TYPE_BEGIN(RangeSectionMap) CDAC_TYPE_INDETERMINATE(RangeSectionMap) CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index f1f4a4d97d60f..a2e0fab6bb2e1 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -68,4 +68,5 @@ public enum DataType ProfControlBlock, ILCodeVersionNode, ReadyToRunInfo, + ImageDataDirectory, } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs index 01d4925dccf26..638e3354dbca2 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs @@ -62,6 +62,7 @@ private enum RangeSectionFlags : int CodeHeap = 0x02, RangeList = 0x04, } + private abstract class JitManager { public Target Target { get; } @@ -72,18 +73,6 @@ protected JitManager(Target target) } public abstract bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info); - - } - - private class ReadyToRunJitManager : JitManager - { - public ReadyToRunJitManager(Target target) : base(target) - { - } - public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) - { - throw new NotImplementedException(); // TODO(cdac): ReadyToRunJitManager::JitCodeToMethodInfo - } } private sealed class RangeSection diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs new file mode 100644 index 0000000000000..fac71f09964f0 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs @@ -0,0 +1,50 @@ +// 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 System.Diagnostics.CodeAnalysis; +using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal readonly partial struct ExecutionManager_1 : IExecutionManager +{ + private class ReadyToRunJitManager : JitManager + { + public ReadyToRunJitManager(Target target) : base(target) + { + } + + public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) + { + // ReadyToRunJitManager::JitCodeToMethodInfo + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + info = default; + Debug.Assert(rangeSection.Data.R2RModule != TargetPointer.Null); + + Data.Module r2rModule = Target.ProcessedData.GetOrAdd(rangeSection.Data.R2RModule); + Debug.Assert(r2rModule.ReadyToRunInfo != TargetPointer.Null); + Data.ReadyToRunInfo r2rInfo = Target.ProcessedData.GetOrAdd(r2rModule.ReadyToRunInfo); + + // Check if address is in a thunk + if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + return false; + + throw new NotImplementedException(); + } + + private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) + { + if (r2rInfo.DelayLoadMethodCallThunks == TargetPointer.Null) + return false; + + // Check if the address is in the region containing thunks for READYTORUN_HELPER_DelayLoad_MethodCall + Data.ImageDataDirectory thunksData = Target.ProcessedData.GetOrAdd(r2rInfo.DelayLoadMethodCallThunks); + ulong rva = jittedCodeAddress - rangeSection.RangeBegin; + return thunksData.VirtualAddress <= rva && rva < thunksData.VirtualAddress + thunksData.Size; + } + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ImageDataDirectory.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ImageDataDirectory.cs new file mode 100644 index 0000000000000..d3717f5eea481 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ImageDataDirectory.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class ImageDataDirectory : IData +{ + static ImageDataDirectory IData.Create(Target target, TargetPointer address) + => new ImageDataDirectory(target, address); + + public ImageDataDirectory(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.ImageDataDirectory); + + VirtualAddress = target.Read(address + (ulong)type.Fields[nameof(VirtualAddress)].Offset); + Size = target.Read(address + (ulong)type.Fields[nameof(Size)].Offset); + } + + public uint VirtualAddress { get; } + public uint Size { get; } +} From f9fb15a19a81d4becda28fc6fb2af866fc3a018e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 13:33:01 -0800 Subject: [PATCH 04/21] ReadyToRunJitManager: find runtime function entry corresponding to address --- .../debug/runtimeinfo/datadescriptor.h | 6 ++ src/coreclr/vm/codeman.cpp | 2 - src/coreclr/vm/codeman.h | 7 +- .../DataType.cs | 1 + ...ExecutionManager_1.ReadyToRunJitManager.cs | 72 +++++++++++++++++++ .../Data/RuntimeFunction.cs | 21 ++++++ 6 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index b25633bfe7ed4..732c29c99a049 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -428,6 +428,12 @@ CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_D CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, Size, offsetof(IMAGE_DATA_DIRECTORY, Size)) CDAC_TYPE_END(ImageDataDirectory) +CDAC_TYPE_BEGIN(RuntimeFunction) +CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION)) +CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress)) +CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress)) +CDAC_TYPE_END(RuntimeFunction) + CDAC_TYPE_BEGIN(RangeSectionMap) CDAC_TYPE_INDETERMINATE(RangeSectionMap) CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index eb774f28fab4b..0769460955ae5 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -5888,8 +5888,6 @@ BOOL ReadyToRunJitManager::JitCodeToMethodInfo(RangeSection * pRangeSection, // Save the raw entry PTR_RUNTIME_FUNCTION RawFunctionEntry = pRuntimeFunctions + MethodIndex; - ULONG UMethodIndex = (ULONG)MethodIndex; - const int lookupIndex = HotColdMappingLookupTable::LookupMappingForMethod(pInfo, (ULONG)MethodIndex); if ((lookupIndex != -1) && ((lookupIndex & 1) == 1)) { diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 3f024f9493c44..4be52c92ea89c 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -23,10 +23,9 @@ Module Name: An IJitManager knows about which method bodies live in each RangeSection. It can handle methods of one given CodeType. It can map a method body to a MethodDesc. It knows where the GCInfo about the method lives. - Today, we have three IJitManagers viz. + Today, we have two IJitManagers: 1. EEJitManager for JITcompiled code generated by clrjit.dll - 2. NativeImageJitManager for ngenned code. - 3. ReadyToRunJitManager for version resiliant ReadyToRun code + 2. ReadyToRunJitManager for version resiliant ReadyToRun code An ICodeManager knows how to crack a specific format of GCInfo. There is a default format (handled by ExecutionManager::GetDefaultCodeManager()) @@ -73,7 +72,6 @@ class MethodDesc; class ICorJitCompiler; class IJitManager; class EEJitManager; -class NativeImageJitManager; class ReadyToRunJitManager; class ExecutionManager; class Thread; @@ -1657,7 +1655,6 @@ class HostCodeHeap; typedef VPTR(class HostCodeHeap) PTR_HostCodeHeap; typedef VPTR(class EEJitManager) PTR_EEJitManager; -typedef VPTR(class NativeImageJitManager) PTR_NativeImageJitManager; typedef VPTR(class ReadyToRunJitManager) PTR_ReadyToRunJitManager; struct JumpStubBlockHeader diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index a2e0fab6bb2e1..760d909901443 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -69,4 +69,5 @@ public enum DataType ILCodeVersionNode, ReadyToRunInfo, ImageDataDirectory, + RuntimeFunction, } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs index fac71f09964f0..e551f04fde143 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs @@ -12,8 +12,11 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; { private class ReadyToRunJitManager : JitManager { + private readonly uint _runtimeFunctionSize; + public ReadyToRunJitManager(Target target) : base(target) { + _runtimeFunctionSize = Target.GetTypeInfo(DataType.RuntimeFunction).Size!.Value; } public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) @@ -33,6 +36,29 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer if (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) return false; + // Find the relative address that we are looking for + TargetCodePointer code = CodePointerUtils.CodePointerFromAddress(jittedCodeAddress.AsTargetPointer, Target); + TargetPointer imageBase = rangeSection.Data.RangeBegin; + TargetPointer relativeAddr = code - imageBase; + + int index = GetRuntimeFunctionIndexForAddress(r2rInfo, relativeAddr); + if (index < 0) + return false; + + bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; + if (featureEHFunclets) + { + // TODO: [cdac] Look up in hot/cold mapping lookup table and if the method is in the cold block, + // get the index of the associated hot block. + // HotColdMappingLookupTable::LookupMappingForMethod + // + // while GetMethodDescForEntryPoint for the begin address of function at index is null + // index-- + } + + TargetPointer functionEntry = r2rInfo.RuntimeFunctions + (ulong)(index * _runtimeFunctionSize); + Data.RuntimeFunction function = Target.ProcessedData.GetOrAdd(functionEntry); + throw new NotImplementedException(); } @@ -46,5 +72,51 @@ private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRu ulong rva = jittedCodeAddress - rangeSection.RangeBegin; return thunksData.VirtualAddress <= rva && rva < thunksData.VirtualAddress + thunksData.Size; } + + private int GetRuntimeFunctionIndexForAddress(Data.ReadyToRunInfo r2rInfo, TargetPointer relativeAddress) + { + // NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod + uint start = 0; + uint end = r2rInfo.NumRuntimeFunctions - 1; + + // Entries are sorted. Binary search until we get to 10 or fewer items. + while (end - start > 10) + { + uint middle = start + (end - start) / 2; + Data.RuntimeFunction func = GetRuntimeFunction(r2rInfo, middle); + if (relativeAddress < func.BeginAddress) + { + end = middle - 1; + } + else + { + start = middle; + } + } + + // Find the runtime function that contains the address of interest + for (uint i = start; i <= end; ++i) + { + // Entries are terminated by a sentinel value of -1, so we can index one past the end safely. + // Read as a runtime function, its begin address is -1 and it fails the check against rel_addr. + // See RuntimeFunctionsTableNode.GetData in RuntimeFunctionsTableNode.cs + Data.RuntimeFunction nextFunc = GetRuntimeFunction(r2rInfo, i + 1); + if (relativeAddress >= nextFunc.BeginAddress) + continue; + + Data.RuntimeFunction func = GetRuntimeFunction(r2rInfo, i); + if (relativeAddress >= func.BeginAddress) + return (int)i; + } + + return -1; + } + + private Data.RuntimeFunction GetRuntimeFunction(Data.ReadyToRunInfo r2rInfo, uint index) + { + TargetPointer first = r2rInfo.RuntimeFunctions; + TargetPointer addr = first + (ulong)(index * _runtimeFunctionSize); + return Target.ProcessedData.GetOrAdd(addr); + } } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs new file mode 100644 index 0000000000000..e05f1e74bde0e --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class RuntimeFunction : IData +{ + static RuntimeFunction IData.Create(Target target, TargetPointer address) + => new RuntimeFunction(target, address); + + public RuntimeFunction(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.RuntimeFunction); + + BeginAddress = target.Read(address + (ulong)type.Fields[nameof(BeginAddress)].Offset); + EndAddress = target.Read(address + (ulong)type.Fields[nameof(EndAddress)].Offset); + } + + public uint BeginAddress { get; } + public uint EndAddress { get; } +} From e73608749e101e40069be917646ce85d09b22a5f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 13:34:09 -0800 Subject: [PATCH 05/21] Add HashMap to data descriptor --- .../debug/runtimeinfo/datadescriptor.h | 13 +++++++++ src/coreclr/vm/hash.cpp | 2 -- src/coreclr/vm/hash.h | 14 +++++++-- .../DataType.cs | 2 ++ .../Constants.cs | 3 ++ .../Data/Bucket.cs | 29 +++++++++++++++++++ .../Data/HashMap.cs | 19 ++++++++++++ 7 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Bucket.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/HashMap.cs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 732c29c99a049..8e34f319a89f0 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -434,6 +434,17 @@ CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNC CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress)) CDAC_TYPE_END(RuntimeFunction) +CDAC_TYPE_BEGIN(HashMap) +CDAC_TYPE_INDETERMINATE(HashMap) +CDAC_TYPE_FIELD(HashMap, /*pointer*/, Buckets, cdac_data::Buckets) +CDAC_TYPE_END(HashMap) + +CDAC_TYPE_BEGIN(Bucket) +CDAC_TYPE_SIZE(sizeof(Bucket)) +CDAC_TYPE_FIELD(Bucket, /*pointer*/, Keys, offsetof(Bucket, m_rgKeys)) +CDAC_TYPE_FIELD(Bucket, /*pointer*/, Values, offsetof(Bucket, m_rgValues)) +CDAC_TYPE_END(Bucket) + CDAC_TYPE_BEGIN(RangeSectionMap) CDAC_TYPE_INDETERMINATE(RangeSectionMap) CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) @@ -523,6 +534,8 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) #endif //TARGET_64BIT 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) +CDAC_GLOBAL(HashMapValueMask, uint64, VALUE_MASK) CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT) CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE) CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data::SyncBlockValue) diff --git a/src/coreclr/vm/hash.cpp b/src/coreclr/vm/hash.cpp index 54791c5fff9ba..c3bb80396e117 100644 --- a/src/coreclr/vm/hash.cpp +++ b/src/coreclr/vm/hash.cpp @@ -34,8 +34,6 @@ const DWORD g_rgPrimes[] = { }; const SIZE_T g_rgNumPrimes = sizeof(g_rgPrimes) / sizeof(*g_rgPrimes); -const unsigned int SLOTS_PER_BUCKET = 4; - #ifndef DACCESS_COMPILE void *PtrHashMap::operator new(size_t size, LoaderHeap *pHeap) diff --git a/src/coreclr/vm/hash.h b/src/coreclr/vm/hash.h index 123993d6ff37a..99e4dfd9ef7d3 100644 --- a/src/coreclr/vm/hash.h +++ b/src/coreclr/vm/hash.h @@ -46,6 +46,8 @@ typedef ULONG_PTR UPTR; class Bucket; class HashMap; +const unsigned int SLOTS_PER_BUCKET = 4; + //------------------------------------------------------- // class Bucket // used by hash table implementation @@ -54,8 +56,8 @@ typedef DPTR(class Bucket) PTR_Bucket; class Bucket { public: - UPTR m_rgKeys[4]; - UPTR m_rgValues[4]; + UPTR m_rgKeys[SLOTS_PER_BUCKET]; + UPTR m_rgValues[SLOTS_PER_BUCKET]; #define VALUE_MASK (sizeof(LPVOID) == 4 ? 0x7FFFFFFF : I64(0x7FFFFFFFFFFFFFFF)) @@ -532,6 +534,14 @@ class HashMap return m_cbInserts-m_cbDeletes; } + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Buckets = offsetof(HashMap, m_rgBuckets); }; //--------------------------------------------------------------------------------------- diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 760d909901443..71076275b7fee 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -70,4 +70,6 @@ public enum DataType ReadyToRunInfo, ImageDataDirectory, RuntimeFunction, + HashMap, + Bucket, } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index 85d29e0eef271..81913e30eaeff 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -43,5 +43,8 @@ internal static class Globals internal const string StubCodeBlockLast = nameof(StubCodeBlockLast); internal const string PlatformMetadata = nameof(PlatformMetadata); internal const string ProfilerControlBlock = nameof(ProfilerControlBlock); + + internal const string HashMapSlotsPerBucket = nameof(HashMapSlotsPerBucket); + internal const string HashMapValueMask = nameof(HashMapValueMask); } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Bucket.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Bucket.cs new file mode 100644 index 0000000000000..a560f36d95714 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Bucket.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class Bucket : IData +{ + static Bucket IData.Create(Target target, TargetPointer address) + => new Bucket(target, address); + + public Bucket(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.Bucket); + ulong keysStart = address + (ulong)type.Fields[nameof(Keys)].Offset; + ulong valuesStart = address + (ulong)type.Fields[nameof(Values)].Offset; + + uint numSlots = target.ReadGlobal(Constants.Globals.HashMapSlotsPerBucket); + Keys = new TargetPointer[numSlots]; + Values = new TargetPointer[numSlots]; + for (int i = 0; i < numSlots; i++) + { + Keys[i] = target.ReadPointer(keysStart + (ulong)(i * target.PointerSize)); + Values[i] = target.ReadPointer(valuesStart + (ulong)(i * target.PointerSize)); + } + } + + public TargetPointer[] Keys { get; } + public TargetPointer[] Values { get; } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/HashMap.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/HashMap.cs new file mode 100644 index 0000000000000..3276fdefd569d --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/HashMap.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class HashMap : IData +{ + static HashMap IData.Create(Target target, TargetPointer address) + => new HashMap(target, address); + + public HashMap(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.HashMap); + + Buckets = target.ReadPointer(address + (ulong)type.Fields[nameof(Buckets)].Offset); + } + + public TargetPointer Buckets { get; } +} From 512f7c22923bbf01cb2b34af3cfcb0b6122bd7ca Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 13:35:35 -0800 Subject: [PATCH 06/21] ReadyToRunJitManager: look up method desc for entry point, add HashMapLookup helper --- .../ExecutionManager/Helpers/HashMapLookup.cs | 84 +++++++++++++++++++ ...ExecutionManager_1.ReadyToRunJitManager.cs | 10 +++ 2 files changed, 94 insertions(+) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs new file mode 100644 index 0000000000000..4b9dd474a7683 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; + +internal sealed class HashMapLookup +{ + public static HashMapLookup Create(Target target) + => new HashMapLookup(target); + + private readonly Target _target; + private readonly ulong _valueMask; + + private HashMapLookup(Target target) + { + _target = target; + _valueMask = target.ReadGlobal(Constants.Globals.HashMapValueMask); + } + + public TargetPointer GetValue(Data.HashMap map, TargetPointer key) + { + // First pointer of Buckets is actually the size + uint size = _target.Read(map.Buckets); + HashFunction(key, size, out uint seed, out uint increment); + + // HashMap::LookupValue + uint bucketSize = _target.GetTypeInfo(DataType.Bucket).Size!.Value; + TargetPointer buckets = map.Buckets + bucketSize; + for (int i = 0; i < size; i++) + { + Data.Bucket bucket = _target.ProcessedData.GetOrAdd(buckets + bucketSize * (seed % size)); + for (int slotIdx = 0; slotIdx < bucket.Keys.Length; slotIdx++) + { + if (bucket.Keys[slotIdx] != key) + continue; + + return bucket.Values[slotIdx] & _valueMask; + } + + seed += increment; + + // We didn't find a match and there is no collision + if (!IsCollision(bucket)) + break; + } + + return TargetPointer.Null; + } + + private static void HashFunction(TargetPointer key, uint size, out uint seed, out uint increment) + { + // HashMap::HashFunction + seed = (uint)(key >> 2); + increment = (uint)(1 + (((uint)(key >> 5) + 1) % (size - 1))); + Debug.Assert(increment > 0 && increment < size); + } + + private bool IsCollision(Data.Bucket bucket) + { + return (bucket.Values[0] & ~_valueMask) != 0; + } +} + +internal sealed class PtrHashMapLookup +{ + public static PtrHashMapLookup Create(Target target) + => new PtrHashMapLookup(target); + + private readonly HashMapLookup _lookup; + private PtrHashMapLookup(Target target) + { + _lookup = HashMapLookup.Create(target); + } + + public TargetPointer GetValue(Data.HashMap map, TargetPointer key) + { + TargetPointer value = _lookup.GetValue(map, key); + + // PtrHashMap shifts values right by one bit when storing. See hash.h + return value << 1; + } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs index e551f04fde143..ba10ae8214a99 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs @@ -13,10 +13,12 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; private class ReadyToRunJitManager : JitManager { private readonly uint _runtimeFunctionSize; + private readonly PtrHashMapLookup _lookup; public ReadyToRunJitManager(Target target) : base(target) { _runtimeFunctionSize = Target.GetTypeInfo(DataType.RuntimeFunction).Size!.Value; + _lookup = PtrHashMapLookup.Create(target); } public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) @@ -59,6 +61,14 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer TargetPointer functionEntry = r2rInfo.RuntimeFunctions + (ulong)(index * _runtimeFunctionSize); Data.RuntimeFunction function = Target.ProcessedData.GetOrAdd(functionEntry); + // ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage + TargetPointer startAddress = imageBase + function.BeginAddress; + TargetPointer entryPoint = CodePointerUtils.CodePointerFromAddress(startAddress, Target).AsTargetPointer; + + Data.HashMap map = Target.ProcessedData.GetOrAdd(r2rInfo.EntryPointToMethodDescMap); + TargetPointer methodDesc = _lookup.GetValue(map, entryPoint); + Debug.Assert(methodDesc != TargetPointer.Null); + throw new NotImplementedException(); } From 3f4793d0de278926c19b2af21d6a4920f8fd282f Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 7 Nov 2024 13:38:02 -0800 Subject: [PATCH 07/21] ReadyToRunJitManager: return CodeBlock, update CodeBlock to take/store a method desc pointer instead of code header --- .../ExecutionManagerBase.EEJitManager.cs | 2 +- .../ExecutionManager/ExecutionManagerBase.cs | 14 +++----------- .../ExecutionManager_1.ReadyToRunJitManager.cs | 7 ++++++- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs index eda3c4e3fb247..2caf4229583eb 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs @@ -42,7 +42,7 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer } TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); Data.RealCodeHeader realCodeHeader = Target.ProcessedData.GetOrAdd(codeHeaderAddress); - info = new CodeBlock(start.Value, codeHeaderOffset, relativeOffset, realCodeHeader, rangeSection.Data!.JitManager); + info = new CodeBlock(start.Value, realCodeHeader.MethodDesc, relativeOffset, rangeSection.Data!.JitManager); return true; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs index 638e3354dbca2..e517aa04f84db 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs @@ -31,28 +31,20 @@ public ExecutionManagerBase(Target target, Data.RangeSectionMap topRangeSectionM } // Note, because of RelativeOffset, this code info is per code pointer, not per method - // TODO: rethink whether this makes sense. We don't need to copy the runtime's notion of EECodeInfo verbatim private class CodeBlock { - private readonly int _codeHeaderOffset; - public TargetCodePointer StartAddress { get; } - // note: this is the address of the pointer to the "real code header", you need to - // dereference it to get the address of _codeHeaderData - public TargetPointer CodeHeaderAddress => StartAddress.Value - (ulong)_codeHeaderOffset; - private Data.RealCodeHeader _codeHeaderData; + public TargetPointer MethodDescAddress { get; } public TargetPointer JitManagerAddress { get; } public TargetNUInt RelativeOffset { get; } - public CodeBlock(TargetCodePointer startAddress, int codeHeaderOffset, TargetNUInt relativeOffset, Data.RealCodeHeader codeHeaderData, TargetPointer jitManagerAddress) + public CodeBlock(TargetCodePointer startAddress, TargetPointer methodDesc, TargetNUInt relativeOffset, TargetPointer jitManagerAddress) { - _codeHeaderOffset = codeHeaderOffset; StartAddress = startAddress; - _codeHeaderData = codeHeaderData; + MethodDescAddress = methodDesc; RelativeOffset = relativeOffset; JitManagerAddress = jitManagerAddress; } - public TargetPointer MethodDescAddress => _codeHeaderData.MethodDesc; public bool Valid => JitManagerAddress != TargetPointer.Null; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs index ba10ae8214a99..e67ce035acc86 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs @@ -69,7 +69,12 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer TargetPointer methodDesc = _lookup.GetValue(map, entryPoint); Debug.Assert(methodDesc != TargetPointer.Null); - throw new NotImplementedException(); + // TODO: [cdac] Handle method with cold code when computing relative offset + // ReadyToRunJitManager::JitTokenToMethodRegionInfo + TargetNUInt relativeOffset = new TargetNUInt(code - startAddress); + + info = new CodeBlock(startAddress.Value, methodDesc, relativeOffset, rangeSection.Data!.JitManager); + return true; } private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRunInfo r2rInfo, TargetCodePointer jittedCodeAddress) From db938d742ffe7b16b3d134f22ab53ff04c3d5b0d Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 15:21:19 -0800 Subject: [PATCH 08/21] Pass map address to HashMapLookup instead of processed data, handle special keys --- .../ExecutionManagerBase.EEJitManager.cs | 19 +++++++-------- .../ExecutionManager/ExecutionManagerBase.cs | 10 ++++---- .../ExecutionManager/Helpers/HashMapLookup.cs | 24 ++++++++++++++----- ...ExecutionManager_1.ReadyToRunJitManager.cs | 3 +-- .../Data/ReadyToRunInfo.cs | 2 +- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs index 2caf4229583eb..4067c714c7eea 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.EEJitManager.cs @@ -23,14 +23,15 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer info = null; // EEJitManager::JitCodeToMethodInfo if (rangeSection.IsRangeList) - { return false; - } + + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + TargetPointer start = FindMethodCode(rangeSection, jittedCodeAddress); if (start == TargetPointer.Null) - { return false; - } + Debug.Assert(start.Value <= jittedCodeAddress.Value); TargetNUInt relativeOffset = new TargetNUInt(jittedCodeAddress.Value - start.Value); // See EEJitManager::GetCodeHeaderFromStartAddress in vm/codeman.h @@ -49,18 +50,14 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointer jittedCodeAddress) { // EEJitManager::FindMethodCode - if (rangeSection.Data == null) - { - throw new InvalidOperationException(); - } + Debug.Assert(rangeSection.Data != null); + if (!rangeSection.IsCodeHeap) - { throw new InvalidOperationException("RangeSection is not a code heap"); - } + TargetPointer heapListAddress = rangeSection.Data.HeapList; Data.CodeHeapListNode heapListNode = Target.ProcessedData.GetOrAdd(heapListAddress); return _nibbleMap.FindMethodCode(heapListNode, jittedCodeAddress); } - } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs index e517aa04f84db..e28c4b30071cf 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.cs @@ -166,18 +166,16 @@ private JitManager GetJitManager(Data.RangeSection rangeSectionData) TargetPointer IExecutionManager.GetMethodDesc(CodeBlockHandle codeInfoHandle) { if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - { - throw new InvalidOperationException("EECodeInfo not found"); - } + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + return info.MethodDescAddress; } TargetCodePointer IExecutionManager.GetStartAddress(CodeBlockHandle codeInfoHandle) { if (!_codeInfos.TryGetValue(codeInfoHandle.Address, out CodeBlock? info)) - { - throw new InvalidOperationException("EECodeInfo not found"); - } + throw new InvalidOperationException($"{nameof(CodeBlock)} not found for {codeInfoHandle.Address}"); + return info.StartAddress; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs index 4b9dd474a7683..6aa9f9e91e1d5 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs @@ -7,6 +7,13 @@ namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; internal sealed class HashMapLookup { + internal enum SpecialKeys + { + Empty = 0, + Deleted = 1, + InvalidEntry = ~0, + } + public static HashMapLookup Create(Target target) => new HashMapLookup(target); @@ -19,9 +26,11 @@ private HashMapLookup(Target target) _valueMask = target.ReadGlobal(Constants.Globals.HashMapValueMask); } - public TargetPointer GetValue(Data.HashMap map, TargetPointer key) + public TargetPointer GetValue(TargetPointer mapAddress, TargetPointer key) { - // First pointer of Buckets is actually the size + Data.HashMap map = _target.ProcessedData.GetOrAdd(mapAddress); + + // First pointer of Buckets is actually the number of buckets uint size = _target.Read(map.Buckets); HashFunction(key, size, out uint seed, out uint increment); @@ -49,7 +58,7 @@ public TargetPointer GetValue(Data.HashMap map, TargetPointer key) return TargetPointer.Null; } - private static void HashFunction(TargetPointer key, uint size, out uint seed, out uint increment) + internal static void HashFunction(TargetPointer key, uint size, out uint seed, out uint increment) { // HashMap::HashFunction seed = (uint)(key >> 2); @@ -74,11 +83,14 @@ private PtrHashMapLookup(Target target) _lookup = HashMapLookup.Create(target); } - public TargetPointer GetValue(Data.HashMap map, TargetPointer key) + public TargetPointer GetValue(TargetPointer mapAddress, TargetPointer key) { - TargetPointer value = _lookup.GetValue(map, key); + // See PtrHashMap::SanitizeKey in hash.h + key = key > (uint)HashMapLookup.SpecialKeys.Deleted ? key : key + 100; + + TargetPointer value = _lookup.GetValue(mapAddress, key); - // PtrHashMap shifts values right by one bit when storing. See hash.h + // PtrHashMap shifts values right by one bit when storing. See PtrHashMap::LookupValue in hash.h return value << 1; } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs index e67ce035acc86..99b4b04ee08d1 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs @@ -65,8 +65,7 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer TargetPointer startAddress = imageBase + function.BeginAddress; TargetPointer entryPoint = CodePointerUtils.CodePointerFromAddress(startAddress, Target).AsTargetPointer; - Data.HashMap map = Target.ProcessedData.GetOrAdd(r2rInfo.EntryPointToMethodDescMap); - TargetPointer methodDesc = _lookup.GetValue(map, entryPoint); + TargetPointer methodDesc = _lookup.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint); Debug.Assert(methodDesc != TargetPointer.Null); // TODO: [cdac] Handle method with cold code when computing relative offset diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs index 2b07deae418f9..e64bf88f26711 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/ReadyToRunInfo.cs @@ -26,7 +26,7 @@ public ReadyToRunInfo(Target target, TargetPointer address) EntryPointToMethodDescMap = CompositeInfo + (ulong)type.Fields[nameof(EntryPointToMethodDescMap)].Offset; } - private TargetPointer CompositeInfo { get; } + internal TargetPointer CompositeInfo { get; } public uint NumRuntimeFunctions { get; } public TargetPointer RuntimeFunctions { get; } From 435e42c737a2419b0562d9910927245c51e9cf14 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 20:08:52 -0800 Subject: [PATCH 09/21] Allow explicitly specifying field size for test helper to layout fields for a type --- .../ExecutionManagerTestBuilder.cs | 36 ++--- .../MockDescriptors.CodeVersions.cs | 24 ++-- .../MockDescriptors.MethodDescriptors.cs | 24 ++-- .../tests/MockDescriptors/MockDescriptors.cs | 127 +++++++++--------- .../cdacreader/tests/PrecodeStubsTests.cs | 22 +-- .../cdacreader/tests/TargetTestHelpers.cs | 16 ++- 6 files changed, 126 insertions(+), 123 deletions(-) diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs index 6a3e32dd49cb6..32e2e993dfdfe 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs @@ -209,48 +209,48 @@ internal ExecutionManagerTestBuilder(int version, MockMemorySpace.Builder builde internal static void AddToTypeInfoCache(TargetTestHelpers targetTestHelpers, Dictionary typeInfoCache) { var layout = targetTestHelpers.LayoutFields([ - (nameof(Data.RangeSectionMap.TopLevelData), DataType.pointer), + new(nameof(Data.RangeSectionMap.TopLevelData), DataType.pointer), ]); typeInfoCache[DataType.RangeSectionMap] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; layout = targetTestHelpers.LayoutFields([ - (nameof(Data.RangeSectionFragment.RangeBegin), DataType.pointer), - (nameof(Data.RangeSectionFragment.RangeEndOpen), DataType.pointer), - (nameof(Data.RangeSectionFragment.RangeSection), DataType.pointer), - (nameof(Data.RangeSectionFragment.Next), DataType.pointer) + new(nameof(Data.RangeSectionFragment.RangeBegin), DataType.pointer), + new(nameof(Data.RangeSectionFragment.RangeEndOpen), DataType.pointer), + new(nameof(Data.RangeSectionFragment.RangeSection), DataType.pointer), + new(nameof(Data.RangeSectionFragment.Next), DataType.pointer) ]); typeInfoCache[DataType.RangeSectionFragment] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; layout = targetTestHelpers.LayoutFields([ - (nameof(Data.RangeSection.RangeBegin), DataType.pointer), - (nameof(Data.RangeSection.RangeEndOpen), DataType.pointer), - (nameof(Data.RangeSection.NextForDelete), DataType.pointer), - (nameof(Data.RangeSection.JitManager), DataType.pointer), - (nameof(Data.RangeSection.Flags), DataType.int32), - (nameof(Data.RangeSection.HeapList), DataType.pointer), - (nameof(Data.RangeSection.R2RModule), DataType.pointer), + new(nameof(Data.RangeSection.RangeBegin), DataType.pointer), + new(nameof(Data.RangeSection.RangeEndOpen), DataType.pointer), + new(nameof(Data.RangeSection.NextForDelete), DataType.pointer), + new(nameof(Data.RangeSection.JitManager), DataType.pointer), + new(nameof(Data.RangeSection.Flags), DataType.int32), + new(nameof(Data.RangeSection.HeapList), DataType.pointer), + new(nameof(Data.RangeSection.R2RModule), DataType.pointer), ]); typeInfoCache[DataType.RangeSection] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; layout = targetTestHelpers.LayoutFields([ - (nameof(Data.CodeHeapListNode.Next), DataType.pointer), - (nameof(Data.CodeHeapListNode.StartAddress), DataType.pointer), - (nameof(Data.CodeHeapListNode.EndAddress), DataType.pointer), - (nameof(Data.CodeHeapListNode.MapBase), DataType.pointer), - (nameof(Data.CodeHeapListNode.HeaderMap), DataType.pointer), + new(nameof(Data.CodeHeapListNode.Next), DataType.pointer), + new(nameof(Data.CodeHeapListNode.StartAddress), DataType.pointer), + new(nameof(Data.CodeHeapListNode.EndAddress), DataType.pointer), + new(nameof(Data.CodeHeapListNode.MapBase), DataType.pointer), + new(nameof(Data.CodeHeapListNode.HeaderMap), DataType.pointer), ]); typeInfoCache[DataType.CodeHeapListNode] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; layout = targetTestHelpers.LayoutFields([ - (nameof(Data.RealCodeHeader.MethodDesc), DataType.pointer), + new(nameof(Data.RealCodeHeader.MethodDesc), DataType.pointer), ]); typeInfoCache[DataType.RealCodeHeader] = new Target.TypeInfo() { Fields = layout.Fields, diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index 98d1986bb01f6..a6b833044abd7 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -19,8 +19,8 @@ public class CodeVersions DataType = DataType.MethodDescVersioningState, Fields = [ - (nameof(Data.MethodDescVersioningState.NativeCodeVersionNode), DataType.pointer), - (nameof(Data.MethodDescVersioningState.Flags), DataType.uint8), + new(nameof(Data.MethodDescVersioningState.NativeCodeVersionNode), DataType.pointer), + new(nameof(Data.MethodDescVersioningState.Flags), DataType.uint8), ] }; @@ -29,11 +29,11 @@ public class CodeVersions DataType = DataType.NativeCodeVersionNode, Fields = [ - (nameof(Data.NativeCodeVersionNode.Next), DataType.pointer), - (nameof(Data.NativeCodeVersionNode.MethodDesc), DataType.pointer), - (nameof(Data.NativeCodeVersionNode.NativeCode), DataType.pointer), - (nameof(Data.NativeCodeVersionNode.Flags), DataType.uint32), - (nameof(Data.NativeCodeVersionNode.ILVersionId), DataType.nuint), + new(nameof(Data.NativeCodeVersionNode.Next), DataType.pointer), + new(nameof(Data.NativeCodeVersionNode.MethodDesc), DataType.pointer), + new(nameof(Data.NativeCodeVersionNode.NativeCode), DataType.pointer), + new(nameof(Data.NativeCodeVersionNode.Flags), DataType.uint32), + new(nameof(Data.NativeCodeVersionNode.ILVersionId), DataType.nuint), ] }; @@ -42,10 +42,10 @@ public class CodeVersions DataType = DataType.ILCodeVersioningState, Fields = [ - (nameof(Data.ILCodeVersioningState.ActiveVersionMethodDef), DataType.uint32), - (nameof(Data.ILCodeVersioningState.ActiveVersionModule), DataType.pointer), - (nameof(Data.ILCodeVersioningState.ActiveVersionKind), DataType.uint32), - (nameof(Data.ILCodeVersioningState.ActiveVersionNode), DataType.pointer), + new(nameof(Data.ILCodeVersioningState.ActiveVersionMethodDef), DataType.uint32), + new(nameof(Data.ILCodeVersioningState.ActiveVersionModule), DataType.pointer), + new(nameof(Data.ILCodeVersioningState.ActiveVersionKind), DataType.uint32), + new(nameof(Data.ILCodeVersioningState.ActiveVersionNode), DataType.pointer), ] }; @@ -54,7 +54,7 @@ public class CodeVersions DataType = DataType.ILCodeVersionNode, Fields = [ - (nameof(Data.ILCodeVersionNode.VersionId), DataType.nuint), + new(nameof(Data.ILCodeVersionNode.VersionId), DataType.nuint), ] }; diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index 35fff02a1d528..4bba2e77b23ec 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -18,12 +18,12 @@ public class MethodDescriptors DataType = DataType.MethodDesc, Fields = [ - (nameof(Data.MethodDesc.ChunkIndex), DataType.uint8), - (nameof(Data.MethodDesc.Slot), DataType.uint16), - (nameof(Data.MethodDesc.Flags), DataType.uint16), - (nameof(Data.MethodDesc.Flags3AndTokenRemainder), DataType.uint16), - (nameof(Data.MethodDesc.EntryPointFlags), DataType.uint8), - (nameof(Data.MethodDesc.CodeData), DataType.pointer), + new(nameof(Data.MethodDesc.ChunkIndex), DataType.uint8), + new(nameof(Data.MethodDesc.Slot), DataType.uint16), + new(nameof(Data.MethodDesc.Flags), DataType.uint16), + new(nameof(Data.MethodDesc.Flags3AndTokenRemainder), DataType.uint16), + new(nameof(Data.MethodDesc.EntryPointFlags), DataType.uint8), + new(nameof(Data.MethodDesc.CodeData), DataType.pointer), ] }; @@ -32,11 +32,11 @@ public class MethodDescriptors DataType = DataType.MethodDescChunk, Fields = [ - (nameof(Data.MethodDescChunk.MethodTable), DataType.pointer), - (nameof(Data.MethodDescChunk.Next), DataType.pointer), - (nameof(Data.MethodDescChunk.Size), DataType.uint8), - (nameof(Data.MethodDescChunk.Count), DataType.uint8), - (nameof(Data.MethodDescChunk.FlagsAndTokenRange), DataType.uint16) + new(nameof(Data.MethodDescChunk.MethodTable), DataType.pointer), + new(nameof(Data.MethodDescChunk.Next), DataType.pointer), + new(nameof(Data.MethodDescChunk.Size), DataType.uint8), + new(nameof(Data.MethodDescChunk.Count), DataType.uint8), + new(nameof(Data.MethodDescChunk.FlagsAndTokenRange), DataType.uint16) ] }; @@ -59,7 +59,7 @@ internal MethodDescriptors(RuntimeTypeSystem rtsBuilder, Loader loaderBuilder) Types = GetTypes(); Globals = rtsBuilder.Globals.Concat( [ - (nameof(Constants.Globals.MethodDescTokenRemainderBitCount), TokenRemainderBitCount, "uint8"), + new(nameof(Constants.Globals.MethodDescTokenRemainderBitCount), TokenRemainderBitCount, "uint8"), ]).ToArray(); } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs index c6f151b896a6a..2e56835e56752 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs @@ -7,10 +7,10 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; internal partial class MockDescriptors { - private record TypeFields + internal record TypeFields { public DataType DataType; - public (string Name, DataType Type)[] Fields; + public TargetTestHelpers.Field[] Fields; public TypeFields BaseTypeFields; } @@ -19,16 +19,16 @@ private record TypeFields DataType = DataType.MethodTable, Fields = [ - (nameof(Data.MethodTable.MTFlags), DataType.uint32), - (nameof(Data.MethodTable.BaseSize), DataType.uint32), - (nameof(Data.MethodTable.MTFlags2), DataType.uint32), - (nameof(Data.MethodTable.EEClassOrCanonMT), DataType.nuint), - (nameof(Data.MethodTable.Module), DataType.pointer), - (nameof(Data.MethodTable.ParentMethodTable), DataType.pointer), - (nameof(Data.MethodTable.NumInterfaces), DataType.uint16), - (nameof(Data.MethodTable.NumVirtuals), DataType.uint16), - (nameof(Data.MethodTable.PerInstInfo), DataType.pointer), - (nameof(Data.MethodTable.AuxiliaryData), DataType.pointer), + new(nameof(Data.MethodTable.MTFlags), DataType.uint32), + new(nameof(Data.MethodTable.BaseSize), DataType.uint32), + new(nameof(Data.MethodTable.MTFlags2), DataType.uint32), + new(nameof(Data.MethodTable.EEClassOrCanonMT), DataType.nuint), + new(nameof(Data.MethodTable.Module), DataType.pointer), + new(nameof(Data.MethodTable.ParentMethodTable), DataType.pointer), + new(nameof(Data.MethodTable.NumInterfaces), DataType.uint16), + new(nameof(Data.MethodTable.NumVirtuals), DataType.uint16), + new(nameof(Data.MethodTable.PerInstInfo), DataType.pointer), + new(nameof(Data.MethodTable.AuxiliaryData), DataType.pointer), ] }; @@ -37,11 +37,11 @@ private record TypeFields DataType = DataType.EEClass, Fields = [ - (nameof(Data.EEClass.MethodTable), DataType.pointer), - (nameof(Data.EEClass.CorTypeAttr), DataType.uint32), - (nameof(Data.EEClass.NumMethods), DataType.uint16), - (nameof(Data.EEClass.InternalCorElementType), DataType.uint8), - (nameof(Data.EEClass.NumNonVirtualSlots), DataType.uint16), + new(nameof(Data.EEClass.MethodTable), DataType.pointer), + new(nameof(Data.EEClass.CorTypeAttr), DataType.uint32), + new(nameof(Data.EEClass.NumMethods), DataType.uint16), + new(nameof(Data.EEClass.InternalCorElementType), DataType.uint8), + new(nameof(Data.EEClass.NumNonVirtualSlots), DataType.uint16), ] }; @@ -50,8 +50,8 @@ private record TypeFields DataType = DataType.MethodTableAuxiliaryData, Fields = [ - (nameof(Data.MethodTableAuxiliaryData.LoaderModule), DataType.pointer), - (nameof(Data.MethodTableAuxiliaryData.OffsetToNonVirtualSlots), DataType.int16), + new(nameof(Data.MethodTableAuxiliaryData.LoaderModule), DataType.pointer), + new(nameof(Data.MethodTableAuxiliaryData.OffsetToNonVirtualSlots), DataType.int16), ] }; @@ -60,7 +60,7 @@ private record TypeFields DataType = DataType.ArrayClass, Fields = [ - (nameof(Data.ArrayClass.Rank), DataType.uint8), + new(nameof(Data.ArrayClass.Rank), DataType.uint8), ], BaseTypeFields = EEClassFields }; @@ -70,7 +70,7 @@ private record TypeFields DataType = DataType.Object, Fields = [ - ("m_pMethTab", DataType.pointer), + new("m_pMethTab", DataType.pointer), ] }; @@ -79,8 +79,8 @@ private record TypeFields DataType = DataType.String, Fields = [ - ("m_StringLength", DataType.uint32), - ("m_FirstChar", DataType.uint16), + new("m_StringLength", DataType.uint32), + new("m_FirstChar", DataType.uint16), ], BaseTypeFields = ObjectFields }; @@ -90,7 +90,7 @@ private record TypeFields DataType = DataType.Array, Fields = [ - ("m_NumComponents", DataType.uint32), + new("m_NumComponents", DataType.uint32), ], BaseTypeFields = ObjectFields }; @@ -100,7 +100,7 @@ private record TypeFields DataType = DataType.SyncTableEntry, Fields = [ - (nameof(Data.SyncTableEntry.SyncBlock), DataType.pointer), + new(nameof(Data.SyncTableEntry.SyncBlock), DataType.pointer), ] }; @@ -109,7 +109,7 @@ private record TypeFields DataType = DataType.SyncBlock, Fields = [ - (nameof(Data.SyncBlock.InteropInfo), DataType.pointer), + new(nameof(Data.SyncBlock.InteropInfo), DataType.pointer), ] }; @@ -118,31 +118,32 @@ private record TypeFields DataType = DataType.InteropSyncBlockInfo, Fields = [ - (nameof(Data.InteropSyncBlockInfo.RCW), DataType.pointer), - (nameof(Data.InteropSyncBlockInfo.CCW), DataType.pointer), + new(nameof(Data.InteropSyncBlockInfo.RCW), DataType.pointer), + new(nameof(Data.InteropSyncBlockInfo.CCW), DataType.pointer), ] }; - private static readonly TypeFields ModuleFields = new TypeFields() + internal static readonly TypeFields ModuleFields = new TypeFields() { DataType = DataType.Module, Fields = [ - (nameof(Data.Module.Assembly), DataType.pointer), - (nameof(Data.Module.Flags), DataType.uint32), - (nameof(Data.Module.Base), DataType.pointer), - (nameof(Data.Module.LoaderAllocator), DataType.pointer), - (nameof(Data.Module.ThunkHeap), DataType.pointer), - (nameof(Data.Module.DynamicMetadata), DataType.pointer), - (nameof(Data.Module.Path), DataType.pointer), - (nameof(Data.Module.FileName), DataType.pointer), - (nameof(Data.Module.FieldDefToDescMap), DataType.pointer), - (nameof(Data.Module.ManifestModuleReferencesMap), DataType.pointer), - (nameof(Data.Module.MemberRefToDescMap), DataType.pointer), - (nameof(Data.Module.MethodDefToDescMap), DataType.pointer), - (nameof(Data.Module.TypeDefToMethodTableMap), DataType.pointer), - (nameof(Data.Module.TypeRefToMethodTableMap), DataType.pointer), - (nameof(Data.Module.MethodDefToILCodeVersioningStateMap), DataType.pointer), + new(nameof(Data.Module.Assembly), DataType.pointer), + new(nameof(Data.Module.Flags), DataType.uint32), + new(nameof(Data.Module.Base), DataType.pointer), + new(nameof(Data.Module.LoaderAllocator), DataType.pointer), + new(nameof(Data.Module.ThunkHeap), DataType.pointer), + new(nameof(Data.Module.DynamicMetadata), DataType.pointer), + new(nameof(Data.Module.Path), DataType.pointer), + new(nameof(Data.Module.FileName), DataType.pointer), + new(nameof(Data.Module.FieldDefToDescMap), DataType.pointer), + new(nameof(Data.Module.ManifestModuleReferencesMap), DataType.pointer), + new(nameof(Data.Module.MemberRefToDescMap), DataType.pointer), + new(nameof(Data.Module.MethodDefToDescMap), DataType.pointer), + new(nameof(Data.Module.TypeDefToMethodTableMap), DataType.pointer), + new(nameof(Data.Module.TypeRefToMethodTableMap), DataType.pointer), + new(nameof(Data.Module.MethodDefToILCodeVersioningStateMap), DataType.pointer), + new(nameof(Data.Module.ReadyToRunInfo), DataType.pointer), ] }; @@ -151,7 +152,7 @@ private record TypeFields DataType = DataType.Assembly, Fields = [ - (nameof(Data.Assembly.IsCollectible), DataType.uint8), + new(nameof(Data.Assembly.IsCollectible), DataType.uint8), ] }; @@ -160,8 +161,8 @@ private record TypeFields DataType = DataType.ExceptionInfo, Fields = [ - (nameof(Data.ExceptionInfo.PreviousNestedInfo), DataType.pointer), - (nameof(Data.ExceptionInfo.ThrownObject), DataType.pointer), + new(nameof(Data.ExceptionInfo.PreviousNestedInfo), DataType.pointer), + new(nameof(Data.ExceptionInfo.ThrownObject), DataType.pointer), ] }; @@ -170,16 +171,16 @@ private record TypeFields DataType = DataType.Thread, Fields = [ - (nameof(Data.Thread.Id), DataType.uint32), - (nameof(Data.Thread.OSId), DataType.nuint), - (nameof(Data.Thread.State), DataType.uint32), - (nameof(Data.Thread.PreemptiveGCDisabled), DataType.uint32), - (nameof(Data.Thread.RuntimeThreadLocals), DataType.pointer), - (nameof(Data.Thread.Frame), DataType.pointer), - (nameof(Data.Thread.TEB), DataType.pointer), - (nameof(Data.Thread.LastThrownObject), DataType.pointer), - (nameof(Data.Thread.LinkNext), DataType.pointer), - (nameof(Data.Thread.ExceptionTracker), DataType.pointer), + new(nameof(Data.Thread.Id), DataType.uint32), + new(nameof(Data.Thread.OSId), DataType.nuint), + new(nameof(Data.Thread.State), DataType.uint32), + new(nameof(Data.Thread.PreemptiveGCDisabled), DataType.uint32), + new(nameof(Data.Thread.RuntimeThreadLocals), DataType.pointer), + new(nameof(Data.Thread.Frame), DataType.pointer), + new(nameof(Data.Thread.TEB), DataType.pointer), + new(nameof(Data.Thread.LastThrownObject), DataType.pointer), + new(nameof(Data.Thread.LinkNext), DataType.pointer), + new(nameof(Data.Thread.ExceptionTracker), DataType.pointer), ] }; @@ -188,12 +189,12 @@ private record TypeFields DataType = DataType.ThreadStore, Fields = [ - (nameof(Data.ThreadStore.ThreadCount), DataType.uint32), - (nameof(Data.ThreadStore.FirstThreadLink), DataType.pointer), - (nameof(Data.ThreadStore.UnstartedCount), DataType.uint32), - (nameof(Data.ThreadStore.BackgroundCount), DataType.uint32), - (nameof(Data.ThreadStore.PendingCount), DataType.uint32), - (nameof(Data.ThreadStore.DeadCount), DataType.uint32), + new(nameof(Data.ThreadStore.ThreadCount), DataType.uint32), + new(nameof(Data.ThreadStore.FirstThreadLink), DataType.pointer), + new(nameof(Data.ThreadStore.UnstartedCount), DataType.uint32), + new(nameof(Data.ThreadStore.BackgroundCount), DataType.uint32), + new(nameof(Data.ThreadStore.PendingCount), DataType.uint32), + new(nameof(Data.ThreadStore.DeadCount), DataType.uint32), ] }; diff --git a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs index 4f2dbb3a32974..b778e1d474c75 100644 --- a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs +++ b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs @@ -198,23 +198,23 @@ public PrecodeBuilder(AllocationRange allocationRange, MockMemorySpace.Builder b public void AddToTypeInfoCache(Dictionary typeInfoCache, TargetTestHelpers targetTestHelpers) { var layout = targetTestHelpers.LayoutFields([ - (nameof(Data.PrecodeMachineDescriptor.StubCodePageSize), DataType.uint32), - (nameof(Data.PrecodeMachineDescriptor.OffsetOfPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.ReadWidthOfPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.ShiftOfPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.InvalidPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.StubPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.PInvokeImportPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.FixupPrecodeType), DataType.uint8), - (nameof(Data.PrecodeMachineDescriptor.ThisPointerRetBufPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.StubCodePageSize), DataType.uint32), + new(nameof(Data.PrecodeMachineDescriptor.OffsetOfPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.ReadWidthOfPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.ShiftOfPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.InvalidPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.StubPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.PInvokeImportPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.FixupPrecodeType), DataType.uint8), + new(nameof(Data.PrecodeMachineDescriptor.ThisPointerRetBufPrecodeType), DataType.uint8), ]); typeInfoCache[DataType.PrecodeMachineDescriptor] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride, }; layout = targetTestHelpers.LayoutFields([ - (nameof(Data.StubPrecodeData.Type), DataType.uint8), - (nameof(Data.StubPrecodeData.MethodDesc), DataType.pointer), + new(nameof(Data.StubPrecodeData.Type), DataType.uint8), + new(nameof(Data.StubPrecodeData.MethodDesc), DataType.pointer), ]); typeInfoCache[DataType.StubPrecodeData] = new Target.TypeInfo() { Fields = layout.Fields, diff --git a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs index 71844fff6f429..41e47ca83766b 100644 --- a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs +++ b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs @@ -333,26 +333,28 @@ public readonly struct LayoutResult public readonly uint MaxAlign { get; init; } } + internal record Field(string Name, DataType Type, uint? Size = null); + // Implements a simple layout algorithm that aligns fields to their size // and aligns the structure to the largest field size. - public LayoutResult LayoutFields((string Name, DataType Type)[] fields) + public LayoutResult LayoutFields(Field[] fields) => LayoutFields(FieldLayout.CIsh, fields); // Layout the fields of a structure according to the specified layout style. - public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type)[] fields) + public LayoutResult LayoutFields(FieldLayout style, Field[] fields) { int offset = 0; int maxAlign = 1; return LayoutFieldsWorker(style, fields, ref offset, ref maxAlign); } - private LayoutResult LayoutFieldsWorker(FieldLayout style, (string Name, DataType Type)[] fields, ref int offset, ref int maxAlign) + private LayoutResult LayoutFieldsWorker(FieldLayout style, Field[] fields, ref int offset, ref int maxAlign) { Dictionary fieldInfos = new (); for (int i = 0; i < fields.Length; i++) { - var (name, type) = fields[i]; - int size = SizeOfPrimitive(type); + var (name, type, sizeMaybe) = fields[i]; + int size = sizeMaybe.HasValue ? (int)sizeMaybe.Value : SizeOfPrimitive(type); int align = size; if (align > maxAlign) { @@ -379,9 +381,9 @@ private LayoutResult LayoutFieldsWorker(FieldLayout style, (string Name, DataTyp } // Extend the layout of a base class with additional fields. - public LayoutResult ExtendLayout((string Name, DataType Type)[] fields, LayoutResult baseClass) => ExtendLayout(FieldLayout.CIsh, fields, baseClass); + public LayoutResult ExtendLayout(Field[] fields, LayoutResult baseClass) => ExtendLayout(FieldLayout.CIsh, fields, baseClass); - public LayoutResult ExtendLayout(FieldLayout fieldLayout, (string Name, DataType Type)[] fields, LayoutResult baseClass) + public LayoutResult ExtendLayout(FieldLayout fieldLayout, Field[] fields, LayoutResult baseClass) { int offset = (int)baseClass.Stride; int maxAlign = (int)baseClass.MaxAlign; From 56418c4f37392cacf47886762fb3a5128e50d993 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 20:11:53 -0800 Subject: [PATCH 10/21] Start using Moq --- .../Properties/InternalsVisibleTo.cs | 7 +++++++ .../tests/ExecutionManager/ExecutionManagerTests.cs | 2 ++ .../Microsoft.Diagnostics.DataContractReader.Tests.csproj | 8 +++++--- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Properties/InternalsVisibleTo.cs diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Properties/InternalsVisibleTo.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Properties/InternalsVisibleTo.cs new file mode 100644 index 0000000000000..444c083967488 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/Properties/InternalsVisibleTo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +// Allows Moq framework to mock internal types (for example, contract interfaces) +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index df151ee05dcac..44ed13d1962f6 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Moq; namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; @@ -33,6 +34,7 @@ public ExecutionManagerTestTarget(int version, MockTarget.Architecture arch, Rea IContractFactory emfactory = new ExecutionManagerFactory(); SetContracts(new TestRegistry() { ExecutionManagerContract = new (() => emfactory.CreateContract(this, version)), + PlatformMetadataContract = new (() => new Mock().Object), }); } public override TargetPointer ReadGlobalPointer(string global) diff --git a/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj b/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj index c12c45e6f1fe8..f00e248f563be 100644 --- a/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj +++ b/src/native/managed/cdacreader/tests/Microsoft.Diagnostics.DataContractReader.Tests.csproj @@ -1,4 +1,4 @@ - + true $(NetCoreAppToolCurrent) @@ -9,8 +9,10 @@ - + + + + From a7066d2ef60c36ae77deccb78460c5e128920663 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 20:12:29 -0800 Subject: [PATCH 11/21] Add test for ExecutionManager.GetMethodDesc for R2R --- .../ExecutionManagerTestBuilder.cs | 105 ++++++++++++ .../ExecutionManager/ExecutionManagerTests.cs | 72 +++++++- .../MockDescriptors.HashMap.cs | 157 ++++++++++++++++++ 3 files changed, 331 insertions(+), 3 deletions(-) create mode 100644 src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs index 32e2e993dfdfe..54661601a29e1 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs @@ -256,6 +256,46 @@ internal static void AddToTypeInfoCache(TargetTestHelpers targetTestHelpers, Dic Fields = layout.Fields, Size = layout.Stride, }; + layout = targetTestHelpers.LayoutFields([ + new(nameof(Data.RuntimeFunction.BeginAddress), DataType.uint32), + new(nameof(Data.RuntimeFunction.EndAddress), DataType.uint32), + ]); + typeInfoCache[DataType.RuntimeFunction] = new Target.TypeInfo() + { + Fields = layout.Fields, + Size = layout.Stride, + }; + layout = targetTestHelpers.LayoutFields(MockDescriptors.HashMap.HashMapFields.Fields); + typeInfoCache[DataType.HashMap] = new Target.TypeInfo() + { + Fields = layout.Fields, + Size = layout.Stride, + }; + uint hashMapSize = layout.Stride; + layout = targetTestHelpers.LayoutFields(MockDescriptors.HashMap.BucketFields(targetTestHelpers).Fields); + typeInfoCache[DataType.Bucket] = new Target.TypeInfo() + { + Fields = layout.Fields, + Size = layout.Stride, + }; + layout = targetTestHelpers.LayoutFields([ + new(nameof(Data.ReadyToRunInfo.CompositeInfo), DataType.pointer), + new(nameof(Data.ReadyToRunInfo.NumRuntimeFunctions), DataType.uint32), + new(nameof(Data.ReadyToRunInfo.RuntimeFunctions), DataType.pointer), + new(nameof(Data.ReadyToRunInfo.DelayLoadMethodCallThunks), DataType.pointer), + new(nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap), DataType.Unknown, hashMapSize), + ]); + typeInfoCache[DataType.ReadyToRunInfo] = new Target.TypeInfo() + { + Fields = layout.Fields, + Size = layout.Stride, + }; + layout = targetTestHelpers.LayoutFields(MockDescriptors.ModuleFields.Fields); + typeInfoCache[MockDescriptors.ModuleFields.DataType] = new Target.TypeInfo() + { + Fields = layout.Fields, + Size = layout.Stride, + }; } internal NibbleMapTestBuilderBase CreateNibbleMap(ulong codeRangeStart, uint codeRangeSize) @@ -306,6 +346,21 @@ public TargetPointer AddRangeSection(JittedCodeRange jittedCodeRange, TargetPoin return rangeSection.Address; } + public TargetPointer AddReadyToRunRangeSection(JittedCodeRange jittedCodeRange, TargetPointer jitManagerAddress, TargetPointer r2rModule) + { + var tyInfo = TypeInfoCache[DataType.RangeSection]; + uint rangeSectionSize = tyInfo.Size.Value; + MockMemorySpace.HeapFragment rangeSection = _rangeSectionMapAllocator.Allocate(rangeSectionSize, "RangeSection"); + Builder.AddHeapFragment(rangeSection); + int pointerSize = Builder.TargetTestHelpers.PointerSize; + Span rs = Builder.BorrowAddressRange(rangeSection.Address, (int)rangeSectionSize); + Builder.TargetTestHelpers.WritePointer(rs.Slice(tyInfo.Fields[nameof(Data.RangeSection.RangeBegin)].Offset, pointerSize), jittedCodeRange.RangeStart); + Builder.TargetTestHelpers.WritePointer(rs.Slice(tyInfo.Fields[nameof(Data.RangeSection.RangeEndOpen)].Offset, pointerSize), jittedCodeRange.RangeEnd); + Builder.TargetTestHelpers.WritePointer(rs.Slice(tyInfo.Fields[nameof(Data.RangeSection.R2RModule)].Offset, pointerSize), r2rModule); + Builder.TargetTestHelpers.WritePointer(rs.Slice(tyInfo.Fields[nameof(Data.RangeSection.JitManager)].Offset, pointerSize), jitManagerAddress); + return rangeSection.Address; + } + public TargetPointer AddRangeSectionFragment(JittedCodeRange jittedCodeRange, TargetPointer rangeSectionAddress) { var tyInfo = TypeInfoCache[DataType.RangeSectionFragment]; @@ -371,5 +426,55 @@ public TargetCodePointer AddJittedMethod(JittedCodeRange jittedCodeRange, uint c return codeStart; } + public TargetPointer AddReadyToRunInfo((uint BeginAddress, uint EndAddress)[] runtimeFunctions) + { + TargetTestHelpers helpers = Builder.TargetTestHelpers; + + // Add the array of runtime functions + uint numRuntimeFunctions = (uint)runtimeFunctions.Length; + Target.TypeInfo runtimeFunctionType = TypeInfoCache[DataType.RuntimeFunction]; + uint runtimeFunctionSize = runtimeFunctionType.Size.Value; + MockMemorySpace.HeapFragment runtimeFunctionsFragment = _codeHeaderAllocator.Allocate((numRuntimeFunctions + 1) * runtimeFunctionSize, $"RuntimeFunctions[{numRuntimeFunctions}]"); + Builder.AddHeapFragment(runtimeFunctionsFragment); + for (uint i = 0; i < numRuntimeFunctions; i++) + { + (uint beginAddress, uint endAddress) = runtimeFunctions[i]; + Span func = Builder.BorrowAddressRange(runtimeFunctionsFragment.Address + i * runtimeFunctionSize, (int)runtimeFunctionSize); + helpers.Write(func.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.BeginAddress)].Offset, sizeof(uint)), beginAddress); + helpers.Write(func.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.EndAddress)].Offset, sizeof(uint)), endAddress); + } + + // Runtime function entries are terminated by a sentinel value of -1 + Span sentinel = Builder.BorrowAddressRange(runtimeFunctionsFragment.Address + numRuntimeFunctions * runtimeFunctionSize, (int)runtimeFunctionSize); + helpers.Write(sentinel.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.BeginAddress)].Offset, sizeof(uint)), ~0u); + + // Add ReadyToRunInfo + Target.TypeInfo r2rInfoType = TypeInfoCache[DataType.ReadyToRunInfo]; + MockMemorySpace.HeapFragment r2rInfo = _codeHeaderAllocator.Allocate(r2rInfoType.Size.Value, "ReadyToRunInfo"); + Builder.AddHeapFragment(r2rInfo); + Span data = r2rInfo.Data; + + // Point composite info at itself + helpers.WritePointer(data.Slice(r2rInfoType.Fields[nameof(Data.ReadyToRunInfo.CompositeInfo)].Offset, helpers.PointerSize), r2rInfo.Address); + + // Point at the runtime functions + helpers.Write(data.Slice(r2rInfoType.Fields[nameof(Data.ReadyToRunInfo.NumRuntimeFunctions)].Offset, sizeof(uint)), numRuntimeFunctions); + helpers.WritePointer(data.Slice(r2rInfoType.Fields[nameof(Data.ReadyToRunInfo.RuntimeFunctions)].Offset, helpers.PointerSize), runtimeFunctionsFragment.Address); + + return r2rInfo.Address; + } + + public TargetPointer AddReadyToRunModule(TargetPointer r2rInfo) + { + TargetTestHelpers helpers = Builder.TargetTestHelpers; + + Target.TypeInfo moduleType = TypeInfoCache[DataType.Module]; + MockMemorySpace.HeapFragment r2rModule = _codeHeaderAllocator.Allocate(moduleType.Size.Value, "R2R Module"); + Builder.AddHeapFragment(r2rModule); + helpers.WritePointer(r2rModule.Data.AsSpan().Slice(moduleType.Fields[nameof(Data.Module.ReadyToRunInfo)].Offset, helpers.PointerSize), r2rInfo); + + return r2rModule.Address; + } + public void MarkCreated() => Builder.MarkCreated(); } diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 44ed13d1962f6..62538be7dee27 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -11,7 +11,6 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; public class ExecutionManagerTests { - internal class ExecutionManagerTestTarget : TestPlaceholderTarget { private readonly ulong _topRangeSectionMap; @@ -56,13 +55,23 @@ public override T ReadGlobal(string name) if (typeof(T) == typeof(byte)) return (T)(object)(byte)0x0Fu; break; + case Constants.Globals.FeatureEHFunclets: + if (typeof(T) == typeof(byte)) + return (T)(object)(byte)1; + break; + case Constants.Globals.HashMapValueMask: + if (typeof(T) == typeof(ulong)) + return (T)(object)(PointerSize == 4 ? 0x7FFFFFFFu : 0x7FFFFFFFFFFFFFFFu); + break; + case Constants.Globals.HashMapSlotsPerBucket: + if (typeof(T) == typeof(uint)) + return (T)(object)4u; + break; default: break; } return base.ReadGlobal(name); - } - } [Theory] @@ -181,6 +190,63 @@ public void LookupNullOneRangeZeroMethod(int version, MockTarget.Architecture ar Assert.Null(eeInfo); } + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary + const uint codeRangeSize = 0xc000u; // arbitrary + + TargetPointer jitManagerAddress = new(0x000b_ff00); // arbitrary + + TargetPointer expectedMethodDescAddress = new TargetPointer(0x0101_aaa0); + + ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + + (uint BeginAddress, uint EndAddress) expectedRuntimeFunction = (0x100, 0x200); + + TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([expectedRuntimeFunction]); + MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); + hashMapBuilder.PopulatePtrMap( + r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + [(jittedCode.RangeStart + expectedRuntimeFunction.BeginAddress, expectedMethodDescAddress)]); + + TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); + TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); + + emBuilder.MarkCreated(); + + TargetCodePointer functionStart = codeRangeStart + expectedRuntimeFunction.BeginAddress; + var target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + + // test + + var em = target.Contracts.ExecutionManager; + Assert.NotNull(em); + + { + var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(expectedMethodDescAddress, actualMethodDesc); + } + { + uint expectedRuntimeFunctionSize = expectedRuntimeFunction.EndAddress - expectedRuntimeFunction.BeginAddress; + var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress + expectedRuntimeFunctionSize / 2); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(expectedMethodDescAddress, actualMethodDesc); + } + { + var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.EndAddress); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(expectedMethodDescAddress, actualMethodDesc); + } + } + public static IEnumerable StdArchAllVersions() { const int highestVersion = 2; diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs new file mode 100644 index 0000000000000..61f83698223cf --- /dev/null +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests; + +internal partial class MockDescriptors +{ + public class HashMap + { + private const uint HashMapSlotsPerBucket = 4; + + internal static readonly TypeFields HashMapFields = new TypeFields() + { + DataType = DataType.HashMap, + Fields = + [ + new (nameof(Data.HashMap.Buckets), DataType.pointer), + ] + }; + + internal static TypeFields BucketFields(TargetTestHelpers helpers) => new TypeFields() + { + DataType = DataType.Bucket, + Fields = + [ + new(nameof(Data.Bucket.Keys), DataType.Unknown, HashMapSlotsPerBucket * (uint)helpers.PointerSize), + new(nameof(Data.Bucket.Values), DataType.Unknown, HashMapSlotsPerBucket * (uint)helpers.PointerSize), + ] + }; + + internal Dictionary Types { get; } + internal (string Name, ulong Value, string? Type)[] Globals { get; } + + private const ulong DefaultAllocationRangeStart = 0x0003_0000; + private const ulong DefaultAllocationRangeEnd = 0x0004_0000; + + // See g_rgPrimes in hash.cpp + private static readonly uint[] PossibleSizes = [5, 11, 17, 23, 29, 37]; + + private readonly MockMemorySpace.Builder _builder; + private readonly MockMemorySpace.BumpAllocator _allocator; + + public HashMap(MockMemorySpace.Builder builder) + : this(builder, (DefaultAllocationRangeStart, DefaultAllocationRangeEnd)) + { } + + public HashMap(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocationRange) + { + _builder = builder; + _allocator = _builder.CreateAllocator(allocationRange.Start, allocationRange.End); + Types = GetTypes(); + Globals = + [ + (nameof(Constants.Globals.HashMapSlotsPerBucket), HashMapSlotsPerBucket, "uint32"), + (nameof(Constants.Globals.HashMapValueMask), _builder.TargetTestHelpers.PointerSize == 4 ? 0x7FFFFFFFu : 0x7FFFFFFFFFFFFFFFu, "uint64"), + ]; + } + + private Dictionary GetTypes() + { + return GetTypesForTypeFields( + _builder.TargetTestHelpers, + [ + HashMapFields, + BucketFields(_builder.TargetTestHelpers), + ]); + } + + public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPointer Value)[] entries) + { + TargetTestHelpers helpers = _builder.TargetTestHelpers; + + // HashMap::NewSize + int requiredSlots = entries.Length * 3 / 2; + uint size = PossibleSizes.Where(i => i > requiredSlots).First(); + + // Allocate the buckets + Target.TypeInfo bucketType = Types[DataType.Bucket]; + uint bucketSize = bucketType.Size!.Value; + + // First bucket is the number of buckets + uint numBuckets = size + 1; + uint totalBucketsSize = bucketSize * numBuckets; + MockMemorySpace.HeapFragment buckets = _allocator.Allocate(totalBucketsSize, $"Buckets[{numBuckets}]"); + _builder.AddHeapFragment(buckets); + helpers.Write(buckets.Data.AsSpan().Slice(0, sizeof(uint)), size); + + const int maxRetry = 8; + foreach ((TargetPointer key, TargetPointer value) in entries) + { + ExecutionManagerHelpers.HashMapLookup.HashFunction(key, size, out uint seed, out uint increment); + + int tryCount = 0; + while (tryCount < maxRetry) + { + Span bucket = buckets.Data.AsSpan().Slice((int)(bucketSize * ((seed % size) + 1))); + if (TryAddEntryToBucket(bucket, key, value)) + break; + + tryCount++; + } + + if (tryCount >= maxRetry) + throw new InvalidOperationException("HashMap test helper does not handle re-hashing"); + } + + // Update the map to point at the buckets + Target.TypeInfo hashMapType = Types[DataType.HashMap]; + Span map = _builder.BorrowAddressRange(mapAddress, (int)hashMapType.Size!.Value); + helpers.WritePointer(map.Slice(hashMapType.Fields[nameof(Data.HashMap.Buckets)].Offset, helpers.PointerSize), buckets.Address); + } + + public void PopulatePtrMap(TargetPointer mapAddress, (TargetPointer Key, TargetPointer Value)[] entries) + { + // PtrHashMap shifts values right by one bit + (TargetPointer Key, TargetPointer Value)[] ptrMapEntries = entries + .Select(e => (e.Key, new TargetPointer(e.Value >> 1))) + .ToArray(); + PopulateMap(mapAddress, ptrMapEntries); + } + + private bool TryAddEntryToBucket(Span bucket, TargetPointer key, TargetPointer value) + { + TargetTestHelpers helpers = _builder.TargetTestHelpers; + Target.TypeInfo bucketType = Types[DataType.Bucket]; + for (int i = 0; i < HashMapSlotsPerBucket; i++) + { + Span keySpan = bucket.Slice(bucketType.Fields[nameof(Data.Bucket.Keys)].Offset + i * helpers.PointerSize, helpers.PointerSize); + if (helpers.ReadPointer(keySpan) != (uint)ExecutionManagerHelpers.HashMapLookup.SpecialKeys.Empty) + continue; + + helpers.WritePointer(keySpan, key); + helpers.WritePointer(bucket.Slice(bucketType.Fields[nameof(Data.Bucket.Values)].Offset + i * helpers.PointerSize, helpers.PointerSize), value); + return true; + } + + // Bucket::SetCollision + ulong valueMask = Globals.Where(g => g.Name == nameof(Constants.Globals.HashMapValueMask)).Select(g => g.Value).First(); + + // Collision bit + Span firstValueSpan = bucket.Slice(bucketType.Fields[nameof(Data.Bucket.Values)].Offset, helpers.PointerSize); + TargetPointer firstValue = helpers.ReadPointer(firstValueSpan); + helpers.WritePointer(firstValueSpan, firstValue | ~valueMask); + + // Has free slots bit + Span secondValueSpan = bucket.Slice(bucketType.Fields[nameof(Data.Bucket.Values)].Offset + helpers.PointerSize, helpers.PointerSize); + TargetPointer secondValue = helpers.ReadPointer(secondValueSpan); + helpers.WritePointer(secondValueSpan, secondValue & valueMask); + + return false; + } + } +} From d2d22c19256ddd9a689b669b9f213d537b0e72ab Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 21:22:23 -0800 Subject: [PATCH 12/21] Fix merge --- .../ExecutionManagerBase.ReadyToRunJitManager.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/{ExecutionManager_1.ReadyToRunJitManager.cs => ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs} (98%) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs similarity index 98% rename from src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs rename to src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs index 99b4b04ee08d1..37a3afb735a6f 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager_1.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs @@ -8,7 +8,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; -internal readonly partial struct ExecutionManager_1 : IExecutionManager +internal partial class ExecutionManagerBase : IExecutionManager { private class ReadyToRunJitManager : JitManager { From 97d4eb432e56a41bd78a9bc50fbfe4e0a4b5c3c4 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 11 Nov 2024 22:25:15 -0800 Subject: [PATCH 13/21] Fix returned value when not found in hash map --- .../ExecutionManager/Helpers/HashMapLookup.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs index 6aa9f9e91e1d5..d039ce9c94eb6 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/Helpers/HashMapLookup.cs @@ -7,11 +7,11 @@ namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; internal sealed class HashMapLookup { - internal enum SpecialKeys + internal enum SpecialKeys : uint { Empty = 0, Deleted = 1, - InvalidEntry = ~0, + InvalidEntry = unchecked((uint)~0), } public static HashMapLookup Create(Target target) @@ -55,7 +55,7 @@ public TargetPointer GetValue(TargetPointer mapAddress, TargetPointer key) break; } - return TargetPointer.Null; + return new TargetPointer((uint)SpecialKeys.InvalidEntry); } internal static void HashFunction(TargetPointer key, uint size, out uint seed, out uint increment) @@ -91,6 +91,8 @@ public TargetPointer GetValue(TargetPointer mapAddress, TargetPointer key) TargetPointer value = _lookup.GetValue(mapAddress, key); // PtrHashMap shifts values right by one bit when storing. See PtrHashMap::LookupValue in hash.h - return value << 1; + return value != (uint)HashMapLookup.SpecialKeys.InvalidEntry + ? value << 1 + : value; } } From 7112857c65c632157ba111d3fbadaf6dd501be9d Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 12 Nov 2024 10:58:29 -0800 Subject: [PATCH 14/21] Tests for no match and multiple runtime functions --- ...ecutionManagerBase.ReadyToRunJitManager.cs | 2 +- .../ExecutionManager/ExecutionManagerTests.cs | 122 ++++++++++++++++-- 2 files changed, 113 insertions(+), 11 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs index 37a3afb735a6f..3ad1b5fadf7fc 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs @@ -112,7 +112,7 @@ private int GetRuntimeFunctionIndexForAddress(Data.ReadyToRunInfo r2rInfo, Targe for (uint i = start; i <= end; ++i) { // Entries are terminated by a sentinel value of -1, so we can index one past the end safely. - // Read as a runtime function, its begin address is -1 and it fails the check against rel_addr. + // Read as a runtime function, its begin address is 0xffffffff (always > relative address). // See RuntimeFunctionsTableNode.GetData in RuntimeFunctionsTableNode.cs Data.RuntimeFunction nextFunc = GetRuntimeFunction(r2rInfo, i + 1); if (relativeAddress >= nextFunc.BeginAddress) diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 62538be7dee27..9b14ae217e1d6 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -76,7 +76,7 @@ public override T ReadGlobal(string name) [Theory] [MemberData(nameof(StdArchAllVersions))] - public void LookupNull(int version, MockTarget.Architecture arch) + public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch) { ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); emBuilder.MarkCreated(); @@ -90,7 +90,7 @@ public void LookupNull(int version, MockTarget.Architecture arch) [Theory] [MemberData(nameof(StdArchAllVersions))] - public void LookupNonNullMissing(int version, MockTarget.Architecture arch) + public void GetCodeBlockHandle_NoRangeSections(int version, MockTarget.Architecture arch) { ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); emBuilder.MarkCreated(); @@ -104,7 +104,7 @@ public void LookupNonNullMissing(int version, MockTarget.Architecture arch) [Theory] [MemberData(nameof(StdArchAllVersions))] - public void LookupNonNullOneRangeOneMethod(int version, MockTarget.Architecture arch) + public void GetMethodDesc_OneRangeOneMethod(int version, MockTarget.Architecture arch) { const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary const uint codeRangeSize = 0xc000u; // arbitrary @@ -154,7 +154,7 @@ public void LookupNonNullOneRangeOneMethod(int version, MockTarget.Architecture [Theory] [MemberData(nameof(StdArchAllVersions))] - public void LookupNullOneRangeZeroMethod(int version, MockTarget.Architecture arch) + public void GetCodeBlockHandle_OneRangeZeroMethod(int version, MockTarget.Architecture arch) { const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary const uint codeRangeSize = 0xc000u; // arbitrary @@ -192,11 +192,45 @@ public void LookupNullOneRangeZeroMethod(int version, MockTarget.Architecture ar [Theory] [MemberData(nameof(StdArchAllVersions))] - public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Architecture arch) + public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarget.Architecture arch) { const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary const uint codeRangeSize = 0xc000u; // arbitrary + TargetPointer jitManagerAddress = new(0x000b_ff00); // arbitrary + + ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + + (uint BeginAddress, uint EndAddress) runtimeFunction = (0x100, 0x200); + + TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([runtimeFunction]); + MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); + hashMapBuilder.PopulatePtrMap( + r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + []); + + TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); + TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); + + emBuilder.MarkCreated(); + + Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + + IExecutionManager em = target.Contracts.ExecutionManager; + Assert.NotNull(em); + // Before any functions + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunction.BeginAddress - 1); + Assert.Null(handle); + } + + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary + const uint codeRangeSize = 0xc000u; // arbitrary TargetPointer jitManagerAddress = new(0x000b_ff00); // arbitrary TargetPointer expectedMethodDescAddress = new TargetPointer(0x0101_aaa0); @@ -218,21 +252,20 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite emBuilder.MarkCreated(); - TargetCodePointer functionStart = codeRangeStart + expectedRuntimeFunction.BeginAddress; - var target = ExecutionManagerTestTarget.FromBuilder(emBuilder); - - // test + Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); - var em = target.Contracts.ExecutionManager; + IExecutionManager em = target.Contracts.ExecutionManager; Assert.NotNull(em); { + // Function start var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(expectedMethodDescAddress, actualMethodDesc); } { + // Function middle uint expectedRuntimeFunctionSize = expectedRuntimeFunction.EndAddress - expectedRuntimeFunction.BeginAddress; var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress + expectedRuntimeFunctionSize / 2); Assert.NotNull(handle); @@ -240,6 +273,7 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite Assert.Equal(expectedMethodDescAddress, actualMethodDesc); } { + // Function end var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.EndAddress); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); @@ -247,6 +281,74 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite } } + [Theory] + [MemberData(nameof(StdArchAllVersions))] + public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.Architecture arch) + { + const ulong codeRangeStart = 0x0a0a_0000u; // arbitrary + const uint codeRangeSize = 0xc000u; // arbitrary + TargetPointer jitManagerAddress = new(0x000b_ff00); // arbitrary + + TargetPointer[] methodDescAddresses = [ 0x0101_aaa0, 0x0201_aaa0]; + + ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); + var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); + + (uint BeginAddress, uint EndAddress)[] runtimeFunctions = [(0x100, 0x200), (0xc00, 0xf00)]; + + TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo(runtimeFunctions); + MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); + hashMapBuilder.PopulatePtrMap( + r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + [ + (jittedCode.RangeStart + runtimeFunctions[0].BeginAddress, methodDescAddresses[0]), + (jittedCode.RangeStart + runtimeFunctions[1].BeginAddress, methodDescAddresses[1]), + ]); + + TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); + TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); + _ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress); + + emBuilder.MarkCreated(); + + Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder); + + IExecutionManager em = target.Contracts.ExecutionManager; + Assert.NotNull(em); + + { + // Match first function + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[0].BeginAddress); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(methodDescAddresses[0], actualMethodDesc); + } + { + // Between first and second - match first function + // Addresses after the end of the function still map to the function as we only check begin addresses + uint betweenFirstAndSecond = runtimeFunctions[0].EndAddress + (runtimeFunctions[1].BeginAddress - runtimeFunctions[0].EndAddress) / 2; + var handle = em.GetCodeBlockHandle(codeRangeStart + betweenFirstAndSecond); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(methodDescAddresses[0], actualMethodDesc); + } + { + // Match second function + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1].BeginAddress); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(methodDescAddresses[1], actualMethodDesc); + } + { + // After second/last function - match second/last function + // Addresses after the end of the function still map to the function as we only check begin addresses + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1].EndAddress + 1); + Assert.NotNull(handle); + TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); + Assert.Equal(methodDescAddresses[1], actualMethodDesc); + } + } + public static IEnumerable StdArchAllVersions() { const int highestVersion = 2; From 12d1fb64248b46e9feecaba40390f006ddfa5501 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 12 Nov 2024 12:51:58 -0800 Subject: [PATCH 15/21] Add HashMap tests --- .../tests/ExecutionManager/HashMapTests.cs | 132 ++++++++++++++++++ .../MockDescriptors.HashMap.cs | 19 +++ 2 files changed, 151 insertions(+) create mode 100644 src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs new file mode 100644 index 0000000000000..f9dcb6d6debb8 --- /dev/null +++ b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; + +namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager; + +public class HashMapTests +{ + internal class HashMapTestTarget : TestPlaceholderTarget + { + private readonly MockMemorySpace.ReadContext _readContext; + private readonly (string Name, ulong Value, string? Type)[] _globals; + + public HashMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext, MockDescriptors.HashMap hashMap) + : base (arch) + { + _readContext = readContext; + _globals = hashMap.Globals; + SetDataCache(new DefaultDataCache(this)); + SetDataReader(_readContext.ReadFromTarget); + SetTypeInfoCache(hashMap.Types); + } + + public override T ReadGlobal(string name) + { + foreach (var global in _globals) + { + if (global.Name == name) + return T.CreateChecked(global.Value); + } + + return base.ReadGlobal(name); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetValue(MockTarget.Architecture arch) + { + MockMemorySpace.Builder builder = new(new TargetTestHelpers(arch)); + MockDescriptors.HashMap hashMap = new(builder); + (TargetPointer Key, TargetPointer Value)[] entries = + [ + (0x100, 0x10), + (0x200, 0x20), + (0x300, 0x30), + (0x400, 0x40), + ]; + TargetPointer mapAddress = hashMap.CreateMap(entries); + TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); + builder.MarkCreated(); + + Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + + var lookup = HashMapLookup.Create(target); + var ptrLookup = PtrHashMapLookup.Create(target); + foreach (var entry in entries) + { + TargetPointer value = lookup.GetValue(mapAddress, entry.Key); + Assert.Equal(entry.Value, value); + + TargetPointer ptrValue = ptrLookup.GetValue(ptrMapAddress, entry.Key); + Assert.Equal(entry.Value, ptrValue); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetValue_Collision(MockTarget.Architecture arch) + { + MockMemorySpace.Builder builder = new(new TargetTestHelpers(arch)); + MockDescriptors.HashMap hashMap = new(builder); + + // Keys are chosen to result in a collision based on HashMapLookup.HashFunction and the size + // of the map (based on the number of entries - see MockDescriptors.HashMap.PopulateMap). + // They result in the same seed and there are more entries than HashMapSlotsPerBucket + (TargetPointer Key, TargetPointer Value) firstEntryDuplicateKey = (0x04, 0x40); + (TargetPointer Key, TargetPointer Value)[] entries = + [ + firstEntryDuplicateKey, + (0x04, 0x41), + (0x05, 0x50), + (0x06, 0x60), + (0x07, 0x70), + ]; + TargetPointer mapAddress = hashMap.CreateMap(entries); + TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); + builder.MarkCreated(); + + Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + + var lookup = HashMapLookup.Create(target); + var ptrLookup = PtrHashMapLookup.Create(target); + foreach (var entry in entries) + { + TargetPointer expectedValue = entry.Key == firstEntryDuplicateKey.Key ? firstEntryDuplicateKey.Value : entry.Value; + TargetPointer value = lookup.GetValue(mapAddress, entry.Key); + Assert.Equal(expectedValue, value); + + TargetPointer ptrValue = ptrLookup.GetValue(ptrMapAddress, entry.Key); + Assert.Equal(expectedValue, ptrValue); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void GetValue_NoMatch(MockTarget.Architecture arch) + { + MockMemorySpace.Builder builder = new(new TargetTestHelpers(arch)); + MockDescriptors.HashMap hashMap = new(builder); + (TargetPointer Key, TargetPointer Value)[] entries = [(0x100, 0x010)]; + TargetPointer mapAddress = hashMap.CreateMap(entries); + TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries); + builder.MarkCreated(); + + Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap); + + { + var lookup = HashMapLookup.Create(target); + TargetPointer value = lookup.GetValue(mapAddress, 0x101); + Assert.Equal((uint)HashMapLookup.SpecialKeys.InvalidEntry, value); + } + { + var lookup = PtrHashMapLookup.Create(target); + TargetPointer value = lookup.GetValue(ptrMapAddress, 0x101); + Assert.Equal((uint)HashMapLookup.SpecialKeys.InvalidEntry, value); + } + } +} diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs index 61f83698223cf..597d75cf6001f 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.HashMap.cs @@ -70,6 +70,15 @@ public HashMap(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocat ]); } + public TargetPointer CreateMap((TargetPointer Key, TargetPointer Value)[] entries) + { + Target.TypeInfo hashMapType = Types[DataType.HashMap]; + MockMemorySpace.HeapFragment map = _allocator.Allocate(hashMapType.Size!.Value, "HashMap"); + _builder.AddHeapFragment(map); + PopulateMap(map.Address, entries); + return map.Address; + } + public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPointer Value)[] entries) { TargetTestHelpers helpers = _builder.TargetTestHelpers; @@ -101,6 +110,7 @@ public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPoin if (TryAddEntryToBucket(bucket, key, value)) break; + seed += increment; tryCount++; } @@ -114,6 +124,15 @@ public void PopulateMap(TargetPointer mapAddress, (TargetPointer Key, TargetPoin helpers.WritePointer(map.Slice(hashMapType.Fields[nameof(Data.HashMap.Buckets)].Offset, helpers.PointerSize), buckets.Address); } + public TargetPointer CreatePtrMap((TargetPointer Key, TargetPointer Value)[] entries) + { + // PtrHashMap shifts values right by one bit + (TargetPointer Key, TargetPointer Value)[] ptrMapEntries = entries + .Select(e => (e.Key, new TargetPointer(e.Value >> 1))) + .ToArray(); + return CreateMap(ptrMapEntries); + } + public void PopulatePtrMap(TargetPointer mapAddress, (TargetPointer Key, TargetPointer Value)[] entries) { // PtrHashMap shifts values right by one bit From 0321eaf08ad3efba7dd3e5a215d92e1fe8901c77 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 12 Nov 2024 13:07:19 -0800 Subject: [PATCH 16/21] Update ExecutionManager.md --- docs/design/datacontracts/ExecutionManager.md | 151 +++++++++++++----- 1 file changed, 113 insertions(+), 38 deletions(-) diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index ea032d3cec32c..940b04acd5e3c 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -28,30 +28,58 @@ struct CodeBlockHandle ## Version 1 The execution manager uses two data structures to map the entire target address space to native executable code. -The range section map is used to partition the address space into large chunks which point to range section fragments. Each chunk is relatively large. If there is any executable code in the chunk, the chunk will contain one or more range section fragments that cover subsets of the chunk. Conversely if a massive method is JITed a single range section fragment may span multiple adjacent chunks. +The [range section map](#rangesectionmap) is used to partition the address space into large chunks which point to range section fragments. Each chunk is relatively large. If there is any executable code in the chunk, the chunk will contain one or more range section fragments that cover subsets of the chunk. Conversely if a massive method is JITed a single range section fragment may span multiple adjacent chunks. -Within a range section fragment, a nibble map structure is used to map arbitrary IP addresses back to the start of the method (and to the code header which immediately preceeeds the entrypoint to the code). +Within a range section fragment, a [nibble map](#nibblemap) structure is used to map arbitrary IP addresses back to the start of the method (and to the code header which immediately preceeeds the entrypoint to the code). Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | -| RangeSectionMap | TopLevelData | pointer to the outermost RangeSection | -| RangeSectionFragment| ? | ? | -| RangeSection | ? | ? | -| RealCodeHeader | ? | ? | -| HeapList | ? | ? | - - +| `RangeSectionMap` | `TopLevelData` | Pointer to the outermost RangeSection | +| `RangeSectionFragment`| `RangeBegin` | Begin address of the fragment | +| `RangeSectionFragment`| `RangeEndOpen` | End address of the fragment | +| `RangeSectionFragment`| `RangeSection` | Pointer to the corresponding `RangeSection` | +| `RangeSectionFragment`| `Next` | Pointer to the next fragment | +| `RangeSection` | `RangeBegin` | Begin address of the range section | +| `RangeSection` | `RangeEndOpen` | End address of the range section | +| `RangeSection` | `NextForDelete` | Pointer to next range section for deletion | +| `RangeSection` | `JitManager` | Pointer to the JIT manager | +| `RangeSection` | `Flags` | Flags for the range section | +| `RangeSection` | `HeapList` | Pointer to the heap list | +| `RangeSection` | `R2RModule` | ReadyToRun module | +| `CodeHeapListNode` | `Next` | Next node | +| `CodeHeapListNode` | `StartAddress` | Start address of the used portion of the code heap | +| `CodeHeapListNode` | `EndAddress` | End address of the used portion of the code heap | +| `CodeHeapListNode` | `MapBase` | Start of the map - start address rounded down based on OS page size | +| `CodeHeapListNode` | `HeaderMap` | Bit array used to find the start of methods - relative to `MapBase` | +| `RealCodeHeader` | `MethodDesc` | Pointer to the corresponding `MethodDesc` | +| `Module` | `ReadyToRunInfo` | Pointer to the `ReadyToRunInfo` for the module | +| `ReadyToRunInfo` | `CompositeInfo` | Pointer to composite R2R info - or itself for non-composite | +| `ReadyToRunInfo` | `NumRuntimeFunctions` | Number of `RuntimeFunctions` | +| `ReadyToRunInfo` | `RuntimeFunctions` | Pointer to an array of `RuntimeFunctions` | +| `ReadyToRunInfo` | `DelayLoadMethodCallThunks` | Pointer to an `ImageDataDirectory` for the delay load method call thunks | +| `ReadyToRunInfo` | `EntryPointToMethodDescMap` | `HashMap` of entry point addresses to `MethodDesc` pointers | +| `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory | +| `ImageDataDirectory` | `Size` | Size of the data | +| `RuntimeFunction` | `BeginAddress` | Begin address of the function | +| `RuntimeFunction` | `EndAddress` | End address of the function | +| `HashMap` | `Buckets` | Pointer to the buckets of a `HashMap` | +| `Bucket` | `Keys` | Array of keys of `HashMapSlotsPerBucket` length | +| `Bucket` | `Values` | Array of values of `HashMapSlotsPerBucket` length | Global variables used: | Global Name | Type | Purpose | | --- | --- | --- | -| ExecutionManagerCodeRangeMapAddress | TargetPointer | Pointer to the global RangeSectionMap -| StubCodeBlockLast | uint8 | Maximum sentinel code header value indentifying a stub code block +| `ExecutionManagerCodeRangeMapAddress` | TargetPointer | Pointer to the global RangeSectionMap | +| `StubCodeBlockLast` | uint8 | Maximum sentinel code header value indentifying a stub code block | +| `HashMapSlotsPerBucket` | uint32 | Number of slots in each bucket of a `HashMap` | +| `HashMapValueMask` | uint64 | Bitmask used when storing values in a `HashMap` | +| `FeatureEHFunclets` | uint8 | 1 if EH funclets are enabled, 0 otherwise | Contracts used: | Contract Name | | --- | +| `PlatformMetadata` | The bulk of the work is done by the `GetCodeBlockHandle` API that maps a code pointer to information about the containing jitted method. @@ -97,28 +125,78 @@ There are two `JitManager`s: the "EE JitManager" for jitted code and "R2R JitMan The EE JitManager `GetMethodInfo` implements the nibble map lookup, summarized below, followed by returning the `RealCodeHeader` data: ```csharp - bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) +bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) +{ + TargetPointer start = FindMethodCode(rangeSection, jittedCodeAddress); // nibble map lookup + if (start == TargetPointer.Null) { - TargetPointer start = FindMethodCode(rangeSection, jittedCodeAddress); // nibble map lookup - if (start == TargetPointer.Null) - { - return false; - } - TargetNUInt relativeOffset = jittedCodeAddress - start; - int codeHeaderOffset = Target.PointerSize; - TargetPointer codeHeaderIndirect = start - codeHeaderOffset; - if (RangeSection.IsStubCodeBlock(Target, codeHeaderIndirect)) - { - return false; - } - TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); - Data.RealCodeHeader realCodeHeader = Target.ProcessedData.GetOrAdd(codeHeaderAddress); - info = new CodeBlock(jittedCodeAddress, codeHeaderOffset, relativeOffset, realCodeHeader, rangeSection.Data!.JitManager); - return true; + return false; + } + TargetNUInt relativeOffset = jittedCodeAddress - start; + int codeHeaderOffset = Target.PointerSize; + TargetPointer codeHeaderIndirect = start - codeHeaderOffset; + if (RangeSection.IsStubCodeBlock(Target, codeHeaderIndirect)) + { + return false; + } + TargetPointer codeHeaderAddress = Target.ReadPointer(codeHeaderIndirect); + Data.RealCodeHeader realCodeHeader = Target.ProcessedData.GetOrAdd(codeHeaderAddress); + info = new CodeBlock(jittedCodeAddress, realCodeHeader.MethodDesc, relativeOffset, rangeSection.Data!.JitManager); + return true; +} +``` + +The R2R JitManager `GetMethodInfo` finds the runtime function corresponding to an address and maps its entry point pack to a method: + +```csharp +bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info) +{ + if (rangeSection.Data == null) + throw new ArgumentException(nameof(rangeSection)); + + 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 (IsStubCodeBlockThunk(rangeSection.Data, r2rInfo, jittedCodeAddress)) + return false; + + // Find the relative address that we are looking for + TargetCodePointer code = /* code pointer from jittedCodeAddress using PlatformMetadata.GetCodePointerFlags */ + TargetPointer imageBase = Target.ReadPointer(/* range section address + RangeSection::RangeBegin offset */); + TargetPointer relativeAddr = code - imageBase; + + TargetPointer runtimeFunctions = Target.ReadPointer(r2rInfo + /* ReadyToRunInfo::RuntimeFunctions offset */); + int index = // Iterate through runtimeFunctions and find index of function with relativeAddress + if (index < 0) + return false; + + bool featureEHFunclets = Target.ReadGlobal(Constants.Globals.FeatureEHFunclets) != 0; + if (featureEHFunclets) + { + // TODO: [cdac] Look up in hot/cold mapping lookup table and if the method is in the cold block, + // get the index of the associated hot block. } + + TargetPointer function = runtimeFunctions + (ulong)(index * /* size of RuntimeFunction */); + + TargetPointer startAddress = imageBase + Target.Read(function + /* RuntimeFunction::BeginAddress offset */); + TargetPointer entryPoint = /* code pointer from startAddress using PlatformMetadata.GetCodePointerFlags */ + + TargetPointer mapAddress = r2rInfo + /* ReadyToRunInfo::EntryPointToMethodDescMap offset */; + TargetPointer methodDesc = /* look up entryPoint in HashMap at mapAddress */; + + // TODO: [cdac] Handle method with cold code when computing relative offset + TargetNUInt relativeOffset = new TargetNUInt(code - startAddress); + + info = new CodeBlock(startAddress.Value, methodDesc, relativeOffset, rangeSection.Data!.JitManager); + return true; +} ``` -The `CodeBlock` encapsulates the `RealCodeHeader` data from the target runtime together with the start of the jitted method +The `CodeBlock` encapsulates the `MethodDesc` data from the target runtime together with the start of the jitted method ```csharp class CodeBlock @@ -126,17 +204,14 @@ class CodeBlock private readonly int _codeHeaderOffset; public TargetCodePointer StartAddress { get; } - // note: this is the address of the pointer to the "real code header", you need to - // dereference it to get the address of _codeHeaderData - public TargetPointer CodeHeaderAddress => StartAddress - _codeHeaderOffset; - private Data.RealCodeHeader _codeHeaderData; + public TargetPointer MethodDesc { get; } public TargetPointer JitManagerAddress { get; } public TargetNUInt RelativeOffset { get; } - public CodeBlock(TargetCodePointer startAddress, int codeHeaderOffset, TargetNUInt relativeOffset, Data.RealCodeHeader codeHeaderData, TargetPointer jitManagerAddress) + + public CodeBlock(TargetCodePointer startAddress, TargetPointer methodDesc, TargetNUInt relativeOffset, TargetPointer jitManagerAddress) { - _codeHeaderOffset = codeHeaderOffset; StartAddress = startAddress; - _codeHeaderData = codeHeaderData; + MethodDesc = methodDesc; RelativeOffset = relativeOffset; JitManagerAddress = jitManagerAddress; } @@ -151,13 +226,13 @@ The remaining contract APIs extract fields of the `CodeBlock`: ```csharp TargetPointer IExecutionManager.GetMethodDesc(CodeBlockHandle codeInfoHandle) { - /* find EECodeBlock info for codeInfoHandle.Address*/ + /* find CodeBlock info for codeInfoHandle.Address*/ return info.MethodDescAddress; } TargetCodePointer IExecutionManager.GetStartAddress(CodeBlockHandle codeInfoHandle) { - /* find EECodeBlock info for codeInfoHandle.Address*/ + /* find CodeBlock info for codeInfoHandle.Address*/ return info.StartAddress; } ``` From 447ce76dd9e6491c46ab668b171fdddeb1081a44 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 12 Nov 2024 16:17:23 -0800 Subject: [PATCH 17/21] Make TestPlaceholderTarget constructor take a reader delegate and always create a default data cache --- .../cdacreader/tests/CodeVersionsTests.cs | 7 ++- .../ExecutionManager/ExecutionManagerTests.cs | 7 ++- .../tests/ExecutionManager/HashMapTests.cs | 6 +-- .../tests/ExecutionManager/NibbleMapTests.cs | 7 +-- .../ExecutionManager/RangeSectionMapTests.cs | 7 +-- .../cdacreader/tests/PrecodeStubsTests.cs | 27 +++++------ .../cdacreader/tests/TestPlaceholderTarget.cs | 45 +++---------------- 7 files changed, 27 insertions(+), 79 deletions(-) diff --git a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs index c2efe33c6fbc4..a762869573143 100644 --- a/src/native/managed/cdacreader/tests/CodeVersionsTests.cs +++ b/src/native/managed/cdacreader/tests/CodeVersionsTests.cs @@ -240,15 +240,14 @@ public CVTestTarget(MockTarget.Architecture arch, IReadOnlyCollection? codeBlocks = null, IReadOnlyCollection? modules = null, ReadFromTargetDelegate reader = null, - Dictionary? typeInfoCache = null) : base(arch) { + Dictionary? typeInfoCache = null) + : base(arch, reader) + { IExecutionManager mockExecutionManager = new MockExecutionManager(codeBlocks ?? []); IRuntimeTypeSystem mockRuntimeTypeSystem = new MockRuntimeTypeSystem(this, methodDescs ?? [], methodTables ?? []); ILoader loader = new MockLoader(modules ?? []); - if (reader != null) - SetDataReader(reader); if (typeInfoCache != null) SetTypeInfoCache(typeInfoCache); - SetDataCache(new DefaultDataCache(this)); IContractFactory cvfactory = new CodeVersionsFactory(); SetContracts(new TestRegistry() { CodeVersionsContract = new (() => cvfactory.CreateContract(this, 1)), diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 9b14ae217e1d6..19c11c8126cf5 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -24,16 +24,15 @@ public static ExecutionManagerTestTarget FromBuilder(ExecutionManagerTestBuilder return new ExecutionManagerTestTarget(emBuilder.Version, arch, reader, topRangeSectionMap, typeInfo); } - public ExecutionManagerTestTarget(int version, MockTarget.Architecture arch, ReadFromTargetDelegate dataReader, TargetPointer topRangeSectionMap, Dictionary typeInfoCache) : base(arch) + public ExecutionManagerTestTarget(int version, MockTarget.Architecture arch, ReadFromTargetDelegate dataReader, TargetPointer topRangeSectionMap, Dictionary typeInfoCache) + : base(arch, dataReader) { _topRangeSectionMap = topRangeSectionMap; - SetDataReader(dataReader); SetTypeInfoCache(typeInfoCache); - SetDataCache(new DefaultDataCache(this)); IContractFactory emfactory = new ExecutionManagerFactory(); SetContracts(new TestRegistry() { ExecutionManagerContract = new (() => emfactory.CreateContract(this, version)), - PlatformMetadataContract = new (() => new Mock().Object), + PlatformMetadataContract = new (() => new Mock().Object) }); } public override TargetPointer ReadGlobalPointer(string global) diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs index f9dcb6d6debb8..61b207abad239 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/HashMapTests.cs @@ -11,16 +11,12 @@ public class HashMapTests { internal class HashMapTestTarget : TestPlaceholderTarget { - private readonly MockMemorySpace.ReadContext _readContext; private readonly (string Name, ulong Value, string? Type)[] _globals; public HashMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext, MockDescriptors.HashMap hashMap) - : base (arch) + : base (arch, readContext.ReadFromTarget) { - _readContext = readContext; _globals = hashMap.Globals; - SetDataCache(new DefaultDataCache(this)); - SetDataReader(_readContext.ReadFromTarget); SetTypeInfoCache(hashMap.Types); } diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs index c92fc3f1afa4c..e9c8369ade410 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/NibbleMapTests.cs @@ -11,13 +11,10 @@ public class NibbleMapTestsBase { internal class NibbleMapTestTarget : TestPlaceholderTarget { - private readonly MockMemorySpace.ReadContext _readContext; - public NibbleMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext) : base(arch) + public NibbleMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext) + : base(arch, readContext.ReadFromTarget) { - _readContext = readContext; - SetDataReader(_readContext.ReadFromTarget); } - } internal static NibbleMapTestTarget CreateTarget(NibbleMapTestBuilderBase nibbleMapTestBuilder) diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs index 36d3a5f9647d0..fed8cc942ad44 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/RangeSectionMapTests.cs @@ -13,17 +13,12 @@ public class RangeSectionMapTests { internal class RSMTestTarget : TestPlaceholderTarget { - private readonly MockMemorySpace.ReadContext _readContext; public RSMTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext) - : base (arch) + : base (arch, readContext.ReadFromTarget) { - _readContext = readContext; - SetDataReader(_readContext.ReadFromTarget); } } - - [Theory] [ClassData(typeof(MockTarget.StdArch))] public void TestLookupFail(MockTarget.Architecture arch) diff --git a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs index b778e1d474c75..1647d5f263952 100644 --- a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs +++ b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; +using Moq; using Microsoft.Diagnostics.DataContractReader.Contracts; using System.Collections.Generic; using System; using System.Reflection; + namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public class PrecodeStubsTests @@ -276,17 +278,6 @@ public TargetCodePointer AddStubPrecodeEntry(string name, PrecodeTestDescriptor internal class PrecodeTestTarget : TestPlaceholderTarget { - private class TestPlatformMetadata : IPlatformMetadata - { - private readonly CodePointerFlags _codePointerFlags; - private readonly TargetPointer _precodeMachineDescriptorAddress; - public TestPlatformMetadata(CodePointerFlags codePointerFlags, TargetPointer precodeMachineDescriptorAddress) { - _codePointerFlags = codePointerFlags; - _precodeMachineDescriptorAddress = precodeMachineDescriptorAddress; - } - TargetPointer IPlatformMetadata.GetPrecodeMachineDescriptor() => _precodeMachineDescriptorAddress; - CodePointerFlags IPlatformMetadata.GetCodePointerFlags() => _codePointerFlags; - } internal readonly TargetPointer PrecodeMachineDescriptorAddress; // hack for this test put the precode machine descriptor at the same address as the PlatformMetadata internal TargetPointer PlatformMetadataAddress => PrecodeMachineDescriptorAddress; @@ -298,16 +289,20 @@ public static PrecodeTestTarget FromBuilder(PrecodeBuilder precodeBuilder) var typeInfo = precodeBuilder.TypeInfoCache; return new PrecodeTestTarget(arch, reader, precodeBuilder.CodePointerFlags, precodeBuilder.MachineDescriptorAddress, typeInfo); } - public PrecodeTestTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader, CodePointerFlags codePointerFlags, TargetPointer platformMetadataAddress, Dictionary typeInfoCache) : base(arch) { + public PrecodeTestTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader, CodePointerFlags codePointerFlags, TargetPointer platformMetadataAddress, Dictionary typeInfoCache) + : base(arch, reader) + { PrecodeMachineDescriptorAddress = platformMetadataAddress; SetTypeInfoCache(typeInfoCache); - SetDataCache(new DefaultDataCache(this)); - SetDataReader(reader); IContractFactory precodeFactory = new PrecodeStubsFactory(); + + Mock platformMetadata = new(MockBehavior.Strict); + platformMetadata.Setup(p => p.GetCodePointerFlags()).Returns(codePointerFlags); + platformMetadata.Setup(p => p.GetPrecodeMachineDescriptor()).Returns(PrecodeMachineDescriptorAddress); + SetContracts(new TestRegistry() { - PlatformMetadataContract = new (() => new TestPlatformMetadata(codePointerFlags, PrecodeMachineDescriptorAddress)), + PlatformMetadataContract = new (() => platformMetadata.Object), PrecodeStubsContract = new (() => precodeFactory.CreateContract(this, 1)), - }); } diff --git a/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs b/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs index 0762f0c95bbf0..7c94e57b011ff 100644 --- a/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs +++ b/src/native/managed/cdacreader/tests/TestPlaceholderTarget.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; +using Moq; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -23,13 +24,14 @@ internal class TestPlaceholderTarget : Target protected ReadFromTargetDelegate _dataReader = (address, buffer) => throw new NotImplementedException(); #region Setup - public TestPlaceholderTarget(MockTarget.Architecture arch) + public TestPlaceholderTarget(MockTarget.Architecture arch, ReadFromTargetDelegate reader) { IsLittleEndian = arch.IsLittleEndian; PointerSize = arch.Is64Bit ? 8 : 4; - contractRegistry = new TestRegistry();; - dataCache = new TestDataCache(); + contractRegistry = new TestRegistry(); + dataCache = new DefaultDataCache(this); typeInfoCache = null; + _dataReader = reader; } internal void SetContracts(ContractRegistry contracts) @@ -37,20 +39,11 @@ internal void SetContracts(ContractRegistry contracts) contractRegistry = contracts; } - internal void SetDataCache(Target.IDataCache cache) - { - dataCache = cache; - } - internal void SetTypeInfoCache(Dictionary cache) { typeInfoCache = cache; } - internal void SetDataReader(ReadFromTargetDelegate reader) - { - _dataReader = reader; - } #endregion Setup public override int PointerSize { get; } @@ -229,34 +222,8 @@ public TestRegistry() { } public override Contracts.IReJIT ReJIT => ReJITContract.Value ?? throw new NotImplementedException(); } - // a data cache that throws NotImplementedException for all methods, - // useful for subclassing to override specific methods - internal class TestDataCache : Target.IDataCache - { - public TestDataCache() {} - - public virtual T GetOrAdd(TargetPointer address) where T : Data.IData - { - if (TryGet(address.Value, out T? data)) - { - return data; - } - return Add(address.Value); - } - - public virtual bool TryGet(ulong address, [NotNullWhen(true)] out T? data) - { - throw new NotImplementedException(); - } - - protected virtual T Add(ulong address) where T : Data.IData - { - throw new NotImplementedException(); - } - } - // A data cache that stores data in a dictionary and calls IData.Create to construct the data. - internal class DefaultDataCache : Target.IDataCache + private class DefaultDataCache : Target.IDataCache { protected readonly Target _target; protected readonly Dictionary<(ulong, Type), object?> _readDataByAddress = []; From a68371d9b6fedbacaad3ed2471331a0bba8abede Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 12 Nov 2024 20:37:13 -0800 Subject: [PATCH 18/21] Clean up ExecutionManagerTestBuilder --- .../ExecutionManagerTestBuilder.cs | 225 +++++++++--------- .../ExecutionManager/ExecutionManagerTests.cs | 8 +- .../tests/MockDescriptors/MockDescriptors.cs | 2 +- 3 files changed, 116 insertions(+), 119 deletions(-) diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs index 54661601a29e1..8a085322ec1ec 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs @@ -23,9 +23,10 @@ public struct AllocationRange // nibble maps for various range section fragments are allocated in this range public ulong NibbleMapStart; public ulong NibbleMapEnd; - // "RealCodeHeader" objects for jitted methods are allocated in this range - public ulong CodeHeaderStart; - public ulong CodeHeaderEnd; + // "RealCodeHeader" objects for jitted methods and the module, info, and runtime functions for R2R + // are allocated in this range + public ulong ExecutionManagerStart; + public ulong ExecutionManagerEnd; } public static readonly AllocationRange DefaultAllocationRange = new AllocationRange { @@ -33,10 +34,10 @@ public struct AllocationRange RangeSectionMapEnd = 0x00de_0000, NibbleMapStart = 0x00ee_0000, NibbleMapEnd = 0x00ef_0000, - CodeHeaderStart = 0x0033_4000, - CodeHeaderEnd = 0x0033_5000, + ExecutionManagerStart = 0x0033_4000, + ExecutionManagerEnd = 0x0033_5000, }; - internal class RangeSectionMapTestBuilder + internal class RangeSectionMapTestBuilder { const ulong DefaultTopLevelAddress = 0x0000_1000u; // arbitrary const int EntriesPerMapLevel = 256; // for now its fixed at 256, see codeman.h RangeSectionMap::entriesPerMapLevel @@ -174,58 +175,32 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect return new RangeSectionMapTestBuilder(arch); } - internal int Version { get;} - - internal MockMemorySpace.Builder Builder { get; } - private readonly RangeSectionMapTestBuilder _rsmBuilder; - - private readonly MockMemorySpace.BumpAllocator _rangeSectionMapAllocator; - private readonly MockMemorySpace.BumpAllocator _nibbleMapAllocator; - private readonly MockMemorySpace.BumpAllocator _codeHeaderAllocator; - - internal readonly Dictionary TypeInfoCache = new(); - - internal ExecutionManagerTestBuilder(int version, MockTarget.Architecture arch, AllocationRange allocationRange) : this(version, new MockMemorySpace.Builder(new TargetTestHelpers(arch)), allocationRange) - {} - - internal ExecutionManagerTestBuilder(int version, MockMemorySpace.Builder builder, AllocationRange allocationRange, Dictionary? typeInfoCache = null) - { - Version = version; - Builder = builder; - _rsmBuilder = new RangeSectionMapTestBuilder(ExecutionManagerCodeRangeMapAddress, builder); - _rangeSectionMapAllocator = Builder.CreateAllocator(allocationRange.RangeSectionMapStart, allocationRange.RangeSectionMapEnd); - _nibbleMapAllocator = Builder.CreateAllocator(allocationRange.NibbleMapStart, allocationRange.NibbleMapEnd); - _codeHeaderAllocator = Builder.CreateAllocator(allocationRange.CodeHeaderStart, allocationRange.CodeHeaderEnd); - TypeInfoCache = typeInfoCache ?? CreateTypeInfoCache(Builder.TargetTestHelpers); - } - - internal static Dictionary CreateTypeInfoCache(TargetTestHelpers targetTestHelpers) + private static readonly MockDescriptors.TypeFields RangeSectionMapFields = new() { - Dictionary typeInfoCache = new(); - AddToTypeInfoCache(targetTestHelpers, typeInfoCache); - return typeInfoCache; - } + DataType = DataType.RangeSectionMap, + Fields = + [ + new(nameof(Data.RangeSectionMap.TopLevelData), DataType.pointer), + ] + }; - internal static void AddToTypeInfoCache(TargetTestHelpers targetTestHelpers, Dictionary typeInfoCache) + private static readonly MockDescriptors.TypeFields RangeSectionFragmentFields = new() { - var layout = targetTestHelpers.LayoutFields([ - new(nameof(Data.RangeSectionMap.TopLevelData), DataType.pointer), - ]); - typeInfoCache[DataType.RangeSectionMap] = new Target.TypeInfo() { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + DataType = DataType.RangeSectionFragment, + Fields = + [ new(nameof(Data.RangeSectionFragment.RangeBegin), DataType.pointer), new(nameof(Data.RangeSectionFragment.RangeEndOpen), DataType.pointer), new(nameof(Data.RangeSectionFragment.RangeSection), DataType.pointer), new(nameof(Data.RangeSectionFragment.Next), DataType.pointer) - ]); - typeInfoCache[DataType.RangeSectionFragment] = new Target.TypeInfo() { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + ] + }; + + private static readonly MockDescriptors.TypeFields RangeSectionFields = new() + { + DataType = DataType.RangeSection, + Fields = + [ new(nameof(Data.RangeSection.RangeBegin), DataType.pointer), new(nameof(Data.RangeSection.RangeEndOpen), DataType.pointer), new(nameof(Data.RangeSection.NextForDelete), DataType.pointer), @@ -233,69 +208,91 @@ internal static void AddToTypeInfoCache(TargetTestHelpers targetTestHelpers, Dic new(nameof(Data.RangeSection.Flags), DataType.int32), new(nameof(Data.RangeSection.HeapList), DataType.pointer), new(nameof(Data.RangeSection.R2RModule), DataType.pointer), - ]); - typeInfoCache[DataType.RangeSection] = new Target.TypeInfo() { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + ] + }; + + private static readonly MockDescriptors.TypeFields CodeHeapListNodeFields = new() + { + DataType = DataType.CodeHeapListNode, + Fields = + [ new(nameof(Data.CodeHeapListNode.Next), DataType.pointer), new(nameof(Data.CodeHeapListNode.StartAddress), DataType.pointer), new(nameof(Data.CodeHeapListNode.EndAddress), DataType.pointer), new(nameof(Data.CodeHeapListNode.MapBase), DataType.pointer), new(nameof(Data.CodeHeapListNode.HeaderMap), DataType.pointer), - ]); - typeInfoCache[DataType.CodeHeapListNode] = new Target.TypeInfo() { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + ] + }; + + private static readonly MockDescriptors.TypeFields RealCodeHeaderFields = new() + { + DataType = DataType.RealCodeHeader, + Fields = + [ new(nameof(Data.RealCodeHeader.MethodDesc), DataType.pointer), - ]); - typeInfoCache[DataType.RealCodeHeader] = new Target.TypeInfo() { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + ] + }; + + private static readonly MockDescriptors.TypeFields RuntimeFunctionFields = new() + { + DataType = DataType.RuntimeFunction, + Fields = + [ new(nameof(Data.RuntimeFunction.BeginAddress), DataType.uint32), new(nameof(Data.RuntimeFunction.EndAddress), DataType.uint32), - ]); - typeInfoCache[DataType.RuntimeFunction] = new Target.TypeInfo() - { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields(MockDescriptors.HashMap.HashMapFields.Fields); - typeInfoCache[DataType.HashMap] = new Target.TypeInfo() - { - Fields = layout.Fields, - Size = layout.Stride, - }; - uint hashMapSize = layout.Stride; - layout = targetTestHelpers.LayoutFields(MockDescriptors.HashMap.BucketFields(targetTestHelpers).Fields); - typeInfoCache[DataType.Bucket] = new Target.TypeInfo() - { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields([ + ] + }; + + private static MockDescriptors.TypeFields ReadyToRunInfoFields(TargetTestHelpers helpers) => new() + { + DataType = DataType.ReadyToRunInfo, + Fields = + [ new(nameof(Data.ReadyToRunInfo.CompositeInfo), DataType.pointer), new(nameof(Data.ReadyToRunInfo.NumRuntimeFunctions), DataType.uint32), new(nameof(Data.ReadyToRunInfo.RuntimeFunctions), DataType.pointer), new(nameof(Data.ReadyToRunInfo.DelayLoadMethodCallThunks), DataType.pointer), - new(nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap), DataType.Unknown, hashMapSize), - ]); - typeInfoCache[DataType.ReadyToRunInfo] = new Target.TypeInfo() - { - Fields = layout.Fields, - Size = layout.Stride, - }; - layout = targetTestHelpers.LayoutFields(MockDescriptors.ModuleFields.Fields); - typeInfoCache[MockDescriptors.ModuleFields.DataType] = new Target.TypeInfo() - { - Fields = layout.Fields, - Size = layout.Stride, - }; + new(nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap), DataType.Unknown, helpers.LayoutFields(MockDescriptors.HashMap.HashMapFields.Fields).Stride), + ] + }; + + internal int Version { get;} + + internal MockMemorySpace.Builder Builder { get; } + internal Dictionary Types { get; } + + private readonly RangeSectionMapTestBuilder _rsmBuilder; + + private readonly MockMemorySpace.BumpAllocator _rangeSectionMapAllocator; + private readonly MockMemorySpace.BumpAllocator _nibbleMapAllocator; + private readonly MockMemorySpace.BumpAllocator _allocator; + + internal ExecutionManagerTestBuilder(int version, MockTarget.Architecture arch, AllocationRange allocationRange) + : this(version, new MockMemorySpace.Builder(new TargetTestHelpers(arch)), allocationRange) + { } + + internal ExecutionManagerTestBuilder(int version, MockMemorySpace.Builder builder, AllocationRange allocationRange) + { + Version = version; + Builder = builder; + _rsmBuilder = new RangeSectionMapTestBuilder(ExecutionManagerCodeRangeMapAddress, builder); + _rangeSectionMapAllocator = Builder.CreateAllocator(allocationRange.RangeSectionMapStart, allocationRange.RangeSectionMapEnd); + _nibbleMapAllocator = Builder.CreateAllocator(allocationRange.NibbleMapStart, allocationRange.NibbleMapEnd); + _allocator = Builder.CreateAllocator(allocationRange.ExecutionManagerStart, allocationRange.ExecutionManagerEnd); + Types = MockDescriptors.GetTypesForTypeFields( + Builder.TargetTestHelpers, + [ + RangeSectionMapFields, + RangeSectionFragmentFields, + RangeSectionFields, + CodeHeapListNodeFields, + RealCodeHeaderFields, + RuntimeFunctionFields, + MockDescriptors.HashMap.HashMapFields, + MockDescriptors.HashMap.BucketFields(Builder.TargetTestHelpers), + ReadyToRunInfoFields(Builder.TargetTestHelpers), + MockDescriptors.ModuleFields, + ]); } internal NibbleMapTestBuilderBase CreateNibbleMap(ulong codeRangeStart, uint codeRangeSize) @@ -329,7 +326,7 @@ public JittedCodeRange AllocateJittedCodeRange(ulong codeRangeStart, uint codeRa public TargetPointer AddRangeSection(JittedCodeRange jittedCodeRange, TargetPointer jitManagerAddress, TargetPointer codeHeapListNodeAddress) { - var tyInfo = TypeInfoCache[DataType.RangeSection]; + var tyInfo = Types[DataType.RangeSection]; uint rangeSectionSize = tyInfo.Size.Value; MockMemorySpace.HeapFragment rangeSection = _rangeSectionMapAllocator.Allocate(rangeSectionSize, "RangeSection"); Builder.AddHeapFragment(rangeSection); @@ -348,7 +345,7 @@ public TargetPointer AddRangeSection(JittedCodeRange jittedCodeRange, TargetPoin public TargetPointer AddReadyToRunRangeSection(JittedCodeRange jittedCodeRange, TargetPointer jitManagerAddress, TargetPointer r2rModule) { - var tyInfo = TypeInfoCache[DataType.RangeSection]; + var tyInfo = Types[DataType.RangeSection]; uint rangeSectionSize = tyInfo.Size.Value; MockMemorySpace.HeapFragment rangeSection = _rangeSectionMapAllocator.Allocate(rangeSectionSize, "RangeSection"); Builder.AddHeapFragment(rangeSection); @@ -363,7 +360,7 @@ public TargetPointer AddReadyToRunRangeSection(JittedCodeRange jittedCodeRange, public TargetPointer AddRangeSectionFragment(JittedCodeRange jittedCodeRange, TargetPointer rangeSectionAddress) { - var tyInfo = TypeInfoCache[DataType.RangeSectionFragment]; + var tyInfo = Types[DataType.RangeSectionFragment]; uint rangeSectionFragmentSize = tyInfo.Size.Value; MockMemorySpace.HeapFragment rangeSectionFragment = _rangeSectionMapAllocator.Allocate(rangeSectionFragmentSize, "RangeSectionFragment"); // FIXME: this shouldn't really be called InsertAddressRange, but maybe InsertRangeSectionFragment? @@ -381,7 +378,7 @@ public TargetPointer AddRangeSectionFragment(JittedCodeRange jittedCodeRange, Ta public TargetPointer AddCodeHeapListNode(TargetPointer next, TargetPointer startAddress, TargetPointer endAddress, TargetPointer mapBase, TargetPointer headerMap) { - var tyInfo = TypeInfoCache[DataType.CodeHeapListNode]; + var tyInfo = Types[DataType.CodeHeapListNode]; uint codeHeapListNodeSize = tyInfo.Size.Value; MockMemorySpace.HeapFragment codeHeapListNode = _rangeSectionMapAllocator.Allocate (codeHeapListNodeSize, "CodeHeapListNode"); Builder.AddHeapFragment(codeHeapListNode); @@ -413,14 +410,14 @@ public TargetCodePointer AddJittedMethod(JittedCodeRange jittedCodeRange, uint c { (MockMemorySpace.HeapFragment methodFragment, TargetCodePointer codeStart) = AllocateJittedMethod(jittedCodeRange, codeSize); - MockMemorySpace.HeapFragment codeHeaderFragment = _codeHeaderAllocator.Allocate(RealCodeHeaderSize, "RealCodeHeader"); + MockMemorySpace.HeapFragment codeHeaderFragment = _allocator.Allocate(RealCodeHeaderSize, "RealCodeHeader"); Builder.AddHeapFragment(codeHeaderFragment); Span mfPtr = Builder.BorrowAddressRange(methodFragment.Address, (int)CodeHeaderSize); Builder.TargetTestHelpers.WritePointer(mfPtr.Slice(0, Builder.TargetTestHelpers.PointerSize), codeHeaderFragment.Address); Span chf = Builder.BorrowAddressRange(codeHeaderFragment.Address, RealCodeHeaderSize); - var tyInfo = TypeInfoCache[DataType.RealCodeHeader]; + var tyInfo = Types[DataType.RealCodeHeader]; Builder.TargetTestHelpers.WritePointer(chf.Slice(tyInfo.Fields[nameof(Data.RealCodeHeader.MethodDesc)].Offset, Builder.TargetTestHelpers.PointerSize), methodDescAddress); return codeStart; @@ -432,9 +429,9 @@ public TargetPointer AddReadyToRunInfo((uint BeginAddress, uint EndAddress)[] ru // Add the array of runtime functions uint numRuntimeFunctions = (uint)runtimeFunctions.Length; - Target.TypeInfo runtimeFunctionType = TypeInfoCache[DataType.RuntimeFunction]; + Target.TypeInfo runtimeFunctionType = Types[DataType.RuntimeFunction]; uint runtimeFunctionSize = runtimeFunctionType.Size.Value; - MockMemorySpace.HeapFragment runtimeFunctionsFragment = _codeHeaderAllocator.Allocate((numRuntimeFunctions + 1) * runtimeFunctionSize, $"RuntimeFunctions[{numRuntimeFunctions}]"); + MockMemorySpace.HeapFragment runtimeFunctionsFragment = _allocator.Allocate((numRuntimeFunctions + 1) * runtimeFunctionSize, $"RuntimeFunctions[{numRuntimeFunctions}]"); Builder.AddHeapFragment(runtimeFunctionsFragment); for (uint i = 0; i < numRuntimeFunctions; i++) { @@ -449,8 +446,8 @@ public TargetPointer AddReadyToRunInfo((uint BeginAddress, uint EndAddress)[] ru helpers.Write(sentinel.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.BeginAddress)].Offset, sizeof(uint)), ~0u); // Add ReadyToRunInfo - Target.TypeInfo r2rInfoType = TypeInfoCache[DataType.ReadyToRunInfo]; - MockMemorySpace.HeapFragment r2rInfo = _codeHeaderAllocator.Allocate(r2rInfoType.Size.Value, "ReadyToRunInfo"); + Target.TypeInfo r2rInfoType = Types[DataType.ReadyToRunInfo]; + MockMemorySpace.HeapFragment r2rInfo = _allocator.Allocate(r2rInfoType.Size.Value, "ReadyToRunInfo"); Builder.AddHeapFragment(r2rInfo); Span data = r2rInfo.Data; @@ -468,8 +465,8 @@ public TargetPointer AddReadyToRunModule(TargetPointer r2rInfo) { TargetTestHelpers helpers = Builder.TargetTestHelpers; - Target.TypeInfo moduleType = TypeInfoCache[DataType.Module]; - MockMemorySpace.HeapFragment r2rModule = _codeHeaderAllocator.Allocate(moduleType.Size.Value, "R2R Module"); + Target.TypeInfo moduleType = Types[DataType.Module]; + MockMemorySpace.HeapFragment r2rModule = _allocator.Allocate(moduleType.Size.Value, "R2R Module"); Builder.AddHeapFragment(r2rModule); helpers.WritePointer(r2rModule.Data.AsSpan().Slice(moduleType.Fields[nameof(Data.Module.ReadyToRunInfo)].Offset, helpers.PointerSize), r2rInfo); diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 19c11c8126cf5..98e11573eada8 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -20,7 +20,7 @@ public static ExecutionManagerTestTarget FromBuilder(ExecutionManagerTestBuilder var arch = emBuilder.Builder.TargetTestHelpers.Arch; ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget; var topRangeSectionMap = ExecutionManagerTestBuilder.ExecutionManagerCodeRangeMapAddress; - var typeInfo = emBuilder.TypeInfoCache; + var typeInfo = emBuilder.Types; return new ExecutionManagerTestTarget(emBuilder.Version, arch, reader, topRangeSectionMap, typeInfo); } @@ -205,7 +205,7 @@ public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarge TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([runtimeFunction]); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); hashMapBuilder.PopulatePtrMap( - r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + r2rInfo + (uint)emBuilder.Types[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, []); TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); @@ -242,7 +242,7 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([expectedRuntimeFunction]); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); hashMapBuilder.PopulatePtrMap( - r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + r2rInfo + (uint)emBuilder.Types[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, [(jittedCode.RangeStart + expectedRuntimeFunction.BeginAddress, expectedMethodDescAddress)]); TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); @@ -298,7 +298,7 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo(runtimeFunctions); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); hashMapBuilder.PopulatePtrMap( - r2rInfo + (uint)emBuilder.TypeInfoCache[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, + r2rInfo + (uint)emBuilder.Types[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, [ (jittedCode.RangeStart + runtimeFunctions[0].BeginAddress, methodDescAddresses[0]), (jittedCode.RangeStart + runtimeFunctions[1].BeginAddress, methodDescAddresses[1]), diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs index 2e56835e56752..cb5bd5271708c 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.cs @@ -198,7 +198,7 @@ internal record TypeFields ] }; - private static Dictionary GetTypesForTypeFields(TargetTestHelpers helpers, TypeFields[] typeFields) + internal static Dictionary GetTypesForTypeFields(TargetTestHelpers helpers, TypeFields[] typeFields) { Dictionary types = new(); foreach (var toAdd in typeFields) From 6377e17b5427cd93037e9f48c7a2e8259a891fe4 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 13 Nov 2024 13:06:13 -0800 Subject: [PATCH 19/21] Remove unused and incorrect field in data descriptor --- docs/design/datacontracts/ExecutionManager.md | 1 - .../debug/runtimeinfo/datadescriptor.h | 1 - .../Data/RuntimeFunction.cs | 2 - .../ExecutionManagerTestBuilder.cs | 7 +--- .../ExecutionManager/ExecutionManagerTests.cs | 40 +++++++------------ 5 files changed, 17 insertions(+), 34 deletions(-) diff --git a/docs/design/datacontracts/ExecutionManager.md b/docs/design/datacontracts/ExecutionManager.md index 940b04acd5e3c..655feda2e2a96 100644 --- a/docs/design/datacontracts/ExecutionManager.md +++ b/docs/design/datacontracts/ExecutionManager.md @@ -62,7 +62,6 @@ Data descriptors used: | `ImageDataDirectory` | `VirtualAddress` | Virtual address of the image data directory | | `ImageDataDirectory` | `Size` | Size of the data | | `RuntimeFunction` | `BeginAddress` | Begin address of the function | -| `RuntimeFunction` | `EndAddress` | End address of the function | | `HashMap` | `Buckets` | Pointer to the buckets of a `HashMap` | | `Bucket` | `Keys` | Array of keys of `HashMapSlotsPerBucket` length | | `Bucket` | `Values` | Array of values of `HashMapSlotsPerBucket` length | diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 8e34f319a89f0..4568395df3cac 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -431,7 +431,6 @@ CDAC_TYPE_END(ImageDataDirectory) CDAC_TYPE_BEGIN(RuntimeFunction) CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION)) CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress)) -CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress)) CDAC_TYPE_END(RuntimeFunction) CDAC_TYPE_BEGIN(HashMap) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs index e05f1e74bde0e..a95cc6e7647fe 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeFunction.cs @@ -13,9 +13,7 @@ public RuntimeFunction(Target target, TargetPointer address) Target.TypeInfo type = target.GetTypeInfo(DataType.RuntimeFunction); BeginAddress = target.Read(address + (ulong)type.Fields[nameof(BeginAddress)].Offset); - EndAddress = target.Read(address + (ulong)type.Fields[nameof(EndAddress)].Offset); } public uint BeginAddress { get; } - public uint EndAddress { get; } } diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs index 8a085322ec1ec..5e2228efd80d9 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTestBuilder.cs @@ -239,7 +239,6 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect Fields = [ new(nameof(Data.RuntimeFunction.BeginAddress), DataType.uint32), - new(nameof(Data.RuntimeFunction.EndAddress), DataType.uint32), ] }; @@ -423,7 +422,7 @@ public TargetCodePointer AddJittedMethod(JittedCodeRange jittedCodeRange, uint c return codeStart; } - public TargetPointer AddReadyToRunInfo((uint BeginAddress, uint EndAddress)[] runtimeFunctions) + public TargetPointer AddReadyToRunInfo(uint[] runtimeFunctions) { TargetTestHelpers helpers = Builder.TargetTestHelpers; @@ -435,10 +434,8 @@ public TargetPointer AddReadyToRunInfo((uint BeginAddress, uint EndAddress)[] ru Builder.AddHeapFragment(runtimeFunctionsFragment); for (uint i = 0; i < numRuntimeFunctions; i++) { - (uint beginAddress, uint endAddress) = runtimeFunctions[i]; Span func = Builder.BorrowAddressRange(runtimeFunctionsFragment.Address + i * runtimeFunctionSize, (int)runtimeFunctionSize); - helpers.Write(func.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.BeginAddress)].Offset, sizeof(uint)), beginAddress); - helpers.Write(func.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.EndAddress)].Offset, sizeof(uint)), endAddress); + helpers.Write(func.Slice(runtimeFunctionType.Fields[nameof(Data.RuntimeFunction.BeginAddress)].Offset, sizeof(uint)), runtimeFunctions[i]); } // Runtime function entries are terminated by a sentinel value of -1 diff --git a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs index 98e11573eada8..78f34095132df 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManager/ExecutionManagerTests.cs @@ -200,7 +200,7 @@ public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarge ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); - (uint BeginAddress, uint EndAddress) runtimeFunction = (0x100, 0x200); + uint runtimeFunction = 0x100; TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([runtimeFunction]); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); @@ -220,7 +220,7 @@ public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarge Assert.NotNull(em); // Before any functions - var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunction.BeginAddress - 1); + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunction - 1); Assert.Null(handle); } @@ -237,13 +237,13 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); - (uint BeginAddress, uint EndAddress) expectedRuntimeFunction = (0x100, 0x200); + uint expectedRuntimeFunction = 0x100; TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo([expectedRuntimeFunction]); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); hashMapBuilder.PopulatePtrMap( r2rInfo + (uint)emBuilder.Types[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, - [(jittedCode.RangeStart + expectedRuntimeFunction.BeginAddress, expectedMethodDescAddress)]); + [(jittedCode.RangeStart + expectedRuntimeFunction, expectedMethodDescAddress)]); TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule); @@ -258,22 +258,14 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite { // Function start - var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress); + var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(expectedMethodDescAddress, actualMethodDesc); } { - // Function middle - uint expectedRuntimeFunctionSize = expectedRuntimeFunction.EndAddress - expectedRuntimeFunction.BeginAddress; - var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.BeginAddress + expectedRuntimeFunctionSize / 2); - Assert.NotNull(handle); - TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); - Assert.Equal(expectedMethodDescAddress, actualMethodDesc); - } - { - // Function end - var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction.EndAddress); + // Past function start + var handle = em.GetCodeBlockHandle(codeRangeStart + expectedRuntimeFunction * 2); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(expectedMethodDescAddress, actualMethodDesc); @@ -293,15 +285,15 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A ExecutionManagerTestBuilder emBuilder = new(version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange); var jittedCode = emBuilder.AllocateJittedCodeRange(codeRangeStart, codeRangeSize); - (uint BeginAddress, uint EndAddress)[] runtimeFunctions = [(0x100, 0x200), (0xc00, 0xf00)]; + uint[] runtimeFunctions = [ 0x100, 0xc00 ]; TargetPointer r2rInfo = emBuilder.AddReadyToRunInfo(runtimeFunctions); MockDescriptors.HashMap hashMapBuilder = new(emBuilder.Builder); hashMapBuilder.PopulatePtrMap( r2rInfo + (uint)emBuilder.Types[DataType.ReadyToRunInfo].Fields[nameof(Data.ReadyToRunInfo.EntryPointToMethodDescMap)].Offset, [ - (jittedCode.RangeStart + runtimeFunctions[0].BeginAddress, methodDescAddresses[0]), - (jittedCode.RangeStart + runtimeFunctions[1].BeginAddress, methodDescAddresses[1]), + (jittedCode.RangeStart + runtimeFunctions[0], methodDescAddresses[0]), + (jittedCode.RangeStart + runtimeFunctions[1], methodDescAddresses[1]), ]); TargetPointer r2rModule = emBuilder.AddReadyToRunModule(r2rInfo); @@ -317,15 +309,14 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A { // Match first function - var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[0].BeginAddress); + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[0]); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(methodDescAddresses[0], actualMethodDesc); } { - // Between first and second - match first function - // Addresses after the end of the function still map to the function as we only check begin addresses - uint betweenFirstAndSecond = runtimeFunctions[0].EndAddress + (runtimeFunctions[1].BeginAddress - runtimeFunctions[0].EndAddress) / 2; + // After first function, before second - match first function + uint betweenFirstAndSecond = runtimeFunctions[0] + (runtimeFunctions[1] - runtimeFunctions[0]) / 2; var handle = em.GetCodeBlockHandle(codeRangeStart + betweenFirstAndSecond); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); @@ -333,15 +324,14 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A } { // Match second function - var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1].BeginAddress); + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1]); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(methodDescAddresses[1], actualMethodDesc); } { // After second/last function - match second/last function - // Addresses after the end of the function still map to the function as we only check begin addresses - var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1].EndAddress + 1); + var handle = em.GetCodeBlockHandle(codeRangeStart + runtimeFunctions[1] * 2); Assert.NotNull(handle); TargetPointer actualMethodDesc = em.GetMethodDesc(handle.Value); Assert.Equal(methodDescAddresses[1], actualMethodDesc); From 6f810601b033cd18c06216aca36c538ccc80ea6e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 13 Nov 2024 16:36:31 -0800 Subject: [PATCH 20/21] Fix handling of code pointer / address conversion --- .../CodePointerUtils.cs | 20 ++++++++++++++++++- ...ecutionManagerBase.ReadyToRunJitManager.cs | 11 +++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs index a6e1763471572..b0b5d10f4ac74 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/CodePointerUtils.cs @@ -9,13 +9,15 @@ namespace Microsoft.Diagnostics.DataContractReader; internal static class CodePointerUtils { + private const uint Arm32ThumbBit = 1; + internal static TargetCodePointer CodePointerFromAddress(TargetPointer address, Target target) { IPlatformMetadata metadata = target.Contracts.PlatformMetadata; CodePointerFlags flags = metadata.GetCodePointerFlags(); if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit)) { - return new TargetCodePointer(address.Value | 1); + return new TargetCodePointer(address.Value | Arm32ThumbBit); } else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth)) { @@ -24,4 +26,20 @@ internal static TargetCodePointer CodePointerFromAddress(TargetPointer address, Debug.Assert(flags == default); return new TargetCodePointer(address.Value); } + + internal static TargetPointer AddressFromCodePointer(TargetCodePointer code, Target target) + { + IPlatformMetadata metadata = target.Contracts.PlatformMetadata; + CodePointerFlags flags = metadata.GetCodePointerFlags(); + if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit)) + { + return new TargetPointer(code.Value & ~Arm32ThumbBit); + } + else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth)) + { + throw new NotImplementedException($"{nameof(AddressFromCodePointer)}: ARM64 with pointer authentication"); + } + Debug.Assert(flags == default); + return new TargetPointer(code.Value); + } } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs index 3ad1b5fadf7fc..68bc4c7493314 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ExecutionManager/ExecutionManagerBase.ReadyToRunJitManager.cs @@ -39,9 +39,9 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer return false; // Find the relative address that we are looking for - TargetCodePointer code = CodePointerUtils.CodePointerFromAddress(jittedCodeAddress.AsTargetPointer, Target); + TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target); TargetPointer imageBase = rangeSection.Data.RangeBegin; - TargetPointer relativeAddr = code - imageBase; + TargetPointer relativeAddr = addr - imageBase; int index = GetRuntimeFunctionIndexForAddress(r2rInfo, relativeAddr); if (index < 0) @@ -62,15 +62,15 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer Data.RuntimeFunction function = Target.ProcessedData.GetOrAdd(functionEntry); // ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage - TargetPointer startAddress = imageBase + function.BeginAddress; - TargetPointer entryPoint = CodePointerUtils.CodePointerFromAddress(startAddress, Target).AsTargetPointer; + TargetCodePointer startAddress = imageBase + function.BeginAddress; + TargetPointer entryPoint = CodePointerUtils.AddressFromCodePointer(startAddress, Target); TargetPointer methodDesc = _lookup.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint); Debug.Assert(methodDesc != TargetPointer.Null); // TODO: [cdac] Handle method with cold code when computing relative offset // ReadyToRunJitManager::JitTokenToMethodRegionInfo - TargetNUInt relativeOffset = new TargetNUInt(code - startAddress); + TargetNUInt relativeOffset = new TargetNUInt(addr - startAddress); info = new CodeBlock(startAddress.Value, methodDesc, relativeOffset, rangeSection.Data!.JitManager); return true; @@ -92,6 +92,7 @@ private int GetRuntimeFunctionIndexForAddress(Data.ReadyToRunInfo r2rInfo, Targe // NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod uint start = 0; uint end = r2rInfo.NumRuntimeFunctions - 1; + relativeAddress = CodePointerUtils.CodePointerFromAddress(relativeAddress, Target).AsTargetPointer; // Entries are sorted. Binary search until we get to 10 or fewer items. while (end - start > 10) From 6d00a3c8790732f182a9664814a163ac356ceee6 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 13 Nov 2024 17:06:20 -0800 Subject: [PATCH 21/21] Update src/coreclr/vm/readytoruninfo.h Co-authored-by: Aaron Robinson --- src/coreclr/vm/readytoruninfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 9a3564798fc6c..2777d5fbc98e3 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -123,7 +123,7 @@ class ReadyToRunInfo PTR_READYTORUN_IMPORT_SECTION m_pImportSections; DWORD m_nImportSections; - bool m_readyToRunCodeDisabled; // Is + bool m_readyToRunCodeDisabled; NativeFormat::NativeReader m_nativeReader; NativeFormat::NativeArray m_methodDefEntryPoints;