From 7def2a4eaa86a0537cc80a5c270d06ab7124b7b2 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 15:10:43 -0400 Subject: [PATCH 01/20] Do automatic field layout for MockObjects.RuntimeTypeSystem --- .../cdacreader/tests/MethodTableTests.cs | 50 +++++----- .../cdacreader/tests/MockDescriptors.cs | 92 ++++++++++--------- .../managed/cdacreader/tests/ObjectTests.cs | 5 +- .../cdacreader/tests/TargetTestHelpers.cs | 8 +- 4 files changed, 81 insertions(+), 74 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index d86c2dd2e8eb2..81cdbd9218e5f 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; +using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; using Xunit; @@ -10,26 +10,28 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; using MockRTS = MockDescriptors.RuntimeTypeSystem; -public unsafe class MethodTableTests +public class MethodTableTests { // a delegate for adding more heap fragments to the context builder - private delegate MockMemorySpace.Builder ConfigureContextBuilder(MockMemorySpace.Builder builder); + private delegate MockMemorySpace.Builder ConfigureContextBuilder(Dictionary types, MockMemorySpace.Builder builder); private static void RTSContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action testCase) { TargetTestHelpers targetTestHelpers = new(arch); MockMemorySpace.Builder builder = new(targetTestHelpers); + Dictionary rtsTypes = new (); + MockRTS.AddTypes(targetTestHelpers, rtsTypes); builder = builder .SetContracts ([ nameof(Contracts.RuntimeTypeSystem) ]) - .SetTypes (MockRTS.Types) + .SetTypes (rtsTypes) .SetGlobals (MockRTS.Globals); - builder = MockRTS.AddGlobalPointers(targetTestHelpers, builder); + builder = MockRTS.AddGlobalPointers(targetTestHelpers, rtsTypes[DataType.MethodTable], builder); if (configure != null) { - builder = configure(builder); + builder = configure(rtsTypes, builder); } bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target); @@ -52,13 +54,13 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) }); } - private static MockMemorySpace.Builder AddSystemObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) + private static MockMemorySpace.Builder AddSystemObject(TargetTestHelpers targetTestHelpers, Dictionary types, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) { System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numMethods = 8; // System.Object has 8 methods const int numVirtuals = 3; // System.Object has 3 virtual methods builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); return builder; @@ -74,9 +76,9 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); TargetTestHelpers targetTestHelpers = new(arch); RTSContractHelper(arch, - (builder) => + (types, builder) => { - builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); return builder; }, (target) => @@ -104,16 +106,16 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) TargetPointer systemStringEEClassPtr = new TargetPointer(SystemStringEEClassAddress); TargetTestHelpers targetTestHelpers = new(arch); RTSContractHelper(arch, - (builder) => + (types, builder) => { - builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed; const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); return builder; @@ -144,10 +146,10 @@ public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) TargetPointer badMethodTablePtr = new TargetPointer(badMethodTableAddress); TargetPointer badMethodTableEEClassPtr = new TargetPointer(badMethodTableEEClassAddress); RTSContractHelper(arch, - (builder) => + (types, builder) => { - builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); + builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); return builder; }, (target) => @@ -179,22 +181,22 @@ public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) const int numMethods = 17; RTSContractHelper(arch, - (builder) => + (types, builder) => { - builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numInterfaces = 0; const int numVirtuals = 3; const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst builder = MockRTS.AddEEClass(targetTestHelpers, builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); const uint ginst_mtflags = 0x00000010; // TODO: GenericsMask_GenericInst TargetPointer ginstCanonMT = new TargetPointer(genericDefinitionMethodTablePtr.Value | (ulong)1); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, mtflags: ginst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: genericDefinitionMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); @@ -235,15 +237,15 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) const uint arrayInstanceComponentSize = 392; RTSContractHelper(arch, - (builder) => + (types, builder) => { - builder = AddSystemObject(targetTestHelpers, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); const ushort systemArrayNumInterfaces = 4; const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); @@ -251,7 +253,7 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); builder = MockRTS.AddEEClass(targetTestHelpers, builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, + builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index f9d393b552f7b..85292c5f8b379 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -12,21 +12,18 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; internal class MockDescriptors { - private static readonly Target.TypeInfo MethodTableTypeInfo = new() + private static readonly (string Name, DataType Type)[] MethodTableFields = new[] { - Fields = new Dictionary { - { nameof(Data.MethodTable.MTFlags), new() { Offset = 4, Type = DataType.uint32}}, - { nameof(Data.MethodTable.BaseSize), new() { Offset = 8, Type = DataType.uint32}}, - { nameof(Data.MethodTable.MTFlags2), new() { Offset = 12, Type = DataType.uint32}}, - { nameof(Data.MethodTable.EEClassOrCanonMT), new () { Offset = 16, Type = DataType.nuint}}, - { nameof(Data.MethodTable.Module), new () { Offset = 24, Type = DataType.pointer}}, - { nameof(Data.MethodTable.ParentMethodTable), new () { Offset = 40, Type = DataType.pointer}}, - { nameof(Data.MethodTable.NumInterfaces), new () { Offset = 48, Type = DataType.uint16}}, - { nameof(Data.MethodTable.NumVirtuals), new () { Offset = 50, Type = DataType.uint16}}, - { nameof(Data.MethodTable.PerInstInfo), new () { Offset = 56, Type = DataType.pointer}}, - } + (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), }; - private static readonly Target.TypeInfo EEClassTypeInfo = new Target.TypeInfo() { Fields = new Dictionary { @@ -143,12 +140,14 @@ public static class RuntimeTypeSystem internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; internal const ulong TestFreeObjectMethodTableAddress = 0x00000000_7a0000a8; - internal static readonly Dictionary Types = new() + internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary types) { - [DataType.MethodTable] = MethodTableTypeInfo, - [DataType.EEClass] = EEClassTypeInfo, - [DataType.ArrayClass] = ArrayClassTypeInfo, - }; + //TODO(cdac): use targetTestHelpers.LayoutFields() + var layout = targetTestHelpers.LayoutFields(MethodTableFields); + types[DataType.MethodTable] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + types[DataType.EEClass] = EEClassTypeInfo; + types[DataType.ArrayClass] = ArrayClassTypeInfo; + } internal static readonly (string Name, ulong Value, string? Type)[] Globals = [ @@ -156,18 +155,18 @@ internal static readonly (string Name, ulong Value, string? Type)[] Globals = (nameof(Constants.Globals.MethodDescAlignment), 8, nameof(DataType.uint64)), ]; - internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { - return AddFreeObjectMethodTable(targetTestHelpers, builder); + return AddFreeObjectMethodTable(targetTestHelpers, methodTableTypeInfo, builder); } - private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of Free Object Method Table", Address = TestFreeObjectMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(globalAddr.Data, TestFreeObjectMethodTableAddress); return builder.AddHeapFragments([ globalAddr, - new () { Name = "Free Object Method Table", Address = TestFreeObjectMethodTableAddress, Data = new byte[targetTestHelpers.SizeOfTypeInfo(MethodTableTypeInfo)] } + new () { Name = "Free Object Method Table", Address = TestFreeObjectMethodTableAddress, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] } ]); } @@ -195,19 +194,19 @@ internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTe return builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, + internal static MockMemorySpace.Builder AddMethodTable(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder, TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, TargetPointer module, TargetPointer parentMethodTable, ushort numInterfaces, ushort numVirtuals) { - MockMemorySpace.HeapFragment methodTableFragment = new() { Name = $"MethodTable '{name}'", Address = methodTablePtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(MethodTableTypeInfo)] }; + MockMemorySpace.HeapFragment methodTableFragment = new() { Name = $"MethodTable '{name}'", Address = methodTablePtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] }; Span dest = methodTableFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset), eeClassOrCanonMT); - targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags)].Offset), mtflags); - targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags2)].Offset), mtflags2); - targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.BaseSize)].Offset), baseSize); - targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.Module)].Offset), module); - targetTestHelpers.WritePointer(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.ParentMethodTable)].Offset), parentMethodTable); - targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.NumInterfaces)].Offset), numInterfaces); - targetTestHelpers.Write(dest.Slice(MethodTableTypeInfo.Fields[nameof(Data.MethodTable.NumVirtuals)].Offset), numVirtuals); + targetTestHelpers.WritePointer(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset), eeClassOrCanonMT); + targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags)].Offset), mtflags); + targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags2)].Offset), mtflags2); + targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.BaseSize)].Offset), baseSize); + targetTestHelpers.WritePointer(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.Module)].Offset), module); + targetTestHelpers.WritePointer(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.ParentMethodTable)].Offset), parentMethodTable); + targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.NumInterfaces)].Offset), numInterfaces); + targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.NumVirtuals)].Offset), numVirtuals); // TODO fill in the rest of the fields return builder.AddHeapFragment(methodTableFragment); @@ -226,16 +225,18 @@ public static class Object internal const ulong TestObjectToMethodTableUnmask = 0x7; internal const ulong TestSyncBlockValueToObjectOffset = sizeof(uint); - internal static Dictionary Types(TargetTestHelpers helpers) => RuntimeTypeSystem.Types.Concat( - new Dictionary() + internal static Dictionary Types(TargetTestHelpers helpers) { - [DataType.Object] = ObjectTypeInfo, - [DataType.String] = StringTypeInfo, - [DataType.Array] = ArrayTypeInfo with { Size = helpers.ArrayBaseSize }, - [DataType.SyncTableEntry] = SyncTableEntryInfo with { Size = (uint)helpers.SizeOfTypeInfo(SyncTableEntryInfo) }, - [DataType.SyncBlock] = SyncBlockTypeInfo, - [DataType.InteropSyncBlockInfo] = InteropSyncBlockTypeInfo, - }).ToDictionary(); + Dictionary types = new(); + RuntimeTypeSystem.AddTypes(helpers, types); + types[DataType.Object] = ObjectTypeInfo; + types[DataType.String] = StringTypeInfo; + types[DataType.Array] = ArrayTypeInfo with { Size = helpers.ArrayBaseSize }; + types[DataType.SyncTableEntry] = SyncTableEntryInfo with { Size = (uint)helpers.SizeOfTypeInfo(SyncTableEntryInfo) }; + types[DataType.SyncBlock] = SyncBlockTypeInfo; + types[DataType.InteropSyncBlockInfo] = InteropSyncBlockTypeInfo; + return types; + } internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHelpers helpers) => RuntimeTypeSystem.Globals.Concat( [ @@ -247,9 +248,9 @@ internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHel (nameof(Constants.Globals.SyncBlockValueToObjectOffset), TestSyncBlockValueToObjectOffset, "uint16"), ]).ToArray(); - internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { - builder = RuntimeTypeSystem.AddGlobalPointers(targetTestHelpers, builder); + builder = RuntimeTypeSystem.AddGlobalPointers(targetTestHelpers, methodTableTypeInfo, builder); builder = AddStringMethodTablePointer(targetTestHelpers, builder); builder = AddSyncTableEntriesPointer(targetTestHelpers, builder); return builder; @@ -354,7 +355,8 @@ internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetT size += array.Rank * sizeof(int) * 2; ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); - ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(RuntimeTypeSystem.Types[DataType.MethodTable]); + Dictionary types = Types(targetTestHelpers); // TODO(cdac): pass in types + ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); uint flags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; if (isSingleDimensionZeroLowerBound) @@ -364,7 +366,7 @@ internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetT builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, builder, arrayClassAddress, name, methodTableAddress, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); - builder = RuntimeTypeSystem.AddMethodTable(targetTestHelpers, builder, methodTableAddress, name, arrayClassAddress, + builder = RuntimeTypeSystem.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0); diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 8a3c05dd79ea1..7ffd305220e12 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -18,12 +18,13 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Configure TargetTestHelpers targetTestHelpers = new(arch); MockMemorySpace.Builder builder = new(targetTestHelpers); + var types = MockObject.Types(targetTestHelpers); builder = builder .SetContracts([ nameof (Contracts.Object), nameof (Contracts.RuntimeTypeSystem) ]) .SetGlobals(MockObject.Globals(targetTestHelpers)) - .SetTypes(MockObject.Types(targetTestHelpers)); + .SetTypes(types); - builder = MockObject.AddGlobalPointers(targetTestHelpers, builder); + builder = MockObject.AddGlobalPointers(targetTestHelpers, types[DataType.MethodTable], builder); if (configure != null) { diff --git a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs index 0c96b19a49f40..68521b1cfab0f 100644 --- a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs +++ b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs @@ -333,12 +333,13 @@ public readonly struct LayoutResult public readonly uint MaxAlign { get; init; } } + // 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) => LayoutFields(FieldLayout.CIsh, fields); - // Implements a simple layout algorithm that aligns fields to their size - // and aligns the structure to the largest field size. - public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type)[] fields) + // Layout the fields of a structure according to the specified layout style. + public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type)[] fields) { Dictionary fieldInfos = new (); int maxAlign = 1; @@ -360,6 +361,7 @@ public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type) }; fieldInfos[name] = new Target.FieldInfo { Offset = offset, + Type = type, }; offset += size; } From 66214cbd8f2d8bf259036ef87ac45863c299acab Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 15:40:06 -0400 Subject: [PATCH 02/20] default BumpAllocator to 16-byte alignment update ExecutionManagerTestBuilder to allocate 1-byte aligned data (same as old BumpAllocator behavior) for the "jitted code" page --- .../tests/ExecutionManagerTestBuilder.cs | 2 +- .../tests/MockMemorySpace.BumpAllocator.cs | 16 ++++++++++++---- .../cdacreader/tests/PrecodeStubsTests.cs | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/native/managed/cdacreader/tests/ExecutionManagerTestBuilder.cs b/src/native/managed/cdacreader/tests/ExecutionManagerTestBuilder.cs index 2438369a8265c..614d63d3f9740 100644 --- a/src/native/managed/cdacreader/tests/ExecutionManagerTestBuilder.cs +++ b/src/native/managed/cdacreader/tests/ExecutionManagerTestBuilder.cs @@ -371,7 +371,7 @@ internal readonly struct JittedCodeRange public JittedCodeRange AllocateJittedCodeRange(ulong codeRangeStart, uint codeRangeSize) { - MockMemorySpace.BumpAllocator allocator = Builder.CreateAllocator(codeRangeStart, codeRangeStart + codeRangeSize); + MockMemorySpace.BumpAllocator allocator = Builder.CreateAllocator(codeRangeStart, codeRangeStart + codeRangeSize, minAlign: 1); return new JittedCodeRange { Allocator = allocator }; } diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs index 5bca9dc508f95..641d58bd2b96c 100644 --- a/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs @@ -25,6 +25,8 @@ internal class BumpAllocator private readonly ulong _blockStart; private readonly ulong _blockEnd; // exclusive ulong _current; + + public int MinAlign { get; init; } = 16; // by default align to 16 bytes public BumpAllocator(ulong blockStart, ulong blockEnd) { _blockStart = blockStart; @@ -35,17 +37,23 @@ public BumpAllocator(ulong blockStart, ulong blockEnd) public ulong RangeStart => _blockStart; public ulong RangeEnd => _blockEnd; + private ulong AlignUp(ulong value) + { + return (value + (ulong)(MinAlign - 1)) & ~(ulong)(MinAlign - 1); + } + public bool TryAllocate(ulong size, string name, [NotNullWhen(true)] out HeapFragment? fragment) { - // FIXME: alignment - if (_current + size <= _blockEnd) + ulong current = AlignUp(_current); + if (current + size <= _blockEnd) { fragment = new HeapFragment { - Address = _current, + Address = current, Data = new byte[size], Name = name, }; - _current += size; + current += size; + _current = current; return true; } fragment = null; diff --git a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs index 1b0abf72fd5f0..296a2c0aae790 100644 --- a/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs +++ b/src/native/managed/cdacreader/tests/PrecodeStubsTests.cs @@ -185,8 +185,8 @@ internal class PrecodeBuilder { } public PrecodeBuilder(AllocationRange allocationRange, MockMemorySpace.Builder builder, Dictionary? typeInfoCache = null) { Builder = builder; - PrecodeAllocator = new MockMemorySpace.BumpAllocator(allocationRange.PrecodeDescriptorStart, allocationRange.PrecodeDescriptorEnd); - StubDataPageAllocator = new MockMemorySpace.BumpAllocator(allocationRange.StubDataPageStart, allocationRange.StubDataPageEnd); + PrecodeAllocator = builder.CreateAllocator(allocationRange.PrecodeDescriptorStart, allocationRange.PrecodeDescriptorEnd); + StubDataPageAllocator = builder.CreateAllocator(allocationRange.StubDataPageStart, allocationRange.StubDataPageEnd); TypeInfoCache = typeInfoCache ?? CreateTypeInfoCache(Builder.TargetTestHelpers); } From 3ce60120f95ef17a9458f08ccdee519e54ca530c Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 15:40:55 -0400 Subject: [PATCH 03/20] use field layout algorithm for EEClass test fields --- .../cdacreader/tests/MethodTableTests.cs | 10 ++--- .../cdacreader/tests/MockDescriptors.cs | 45 +++++++++---------- .../cdacreader/tests/MockMemorySpace.cs | 4 +- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 81cdbd9218e5f..127ca26cfa0cb 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -59,7 +59,7 @@ private static MockMemorySpace.Builder AddSystemObject(TargetTestHelpers targetT System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numMethods = 8; // System.Object has 8 methods const int numVirtuals = 3; // System.Object has 3 virtual methods - builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); @@ -114,7 +114,7 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; - builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); @@ -189,7 +189,7 @@ public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) const int numInterfaces = 0; const int numVirtuals = 3; const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst - builder = MockRTS.AddEEClass(targetTestHelpers, builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); @@ -244,7 +244,7 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); - builder = MockRTS.AddEEClass(targetTestHelpers, builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); @@ -252,7 +252,7 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); - builder = MockRTS.AddEEClass(targetTestHelpers, builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 85292c5f8b379..6f0b74f44428c 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -24,17 +24,15 @@ private static readonly (string Name, DataType Type)[] MethodTableFields = new[] (nameof(Data.MethodTable.NumVirtuals), DataType.uint16), (nameof(Data.MethodTable.PerInstInfo), DataType.pointer), }; - private static readonly Target.TypeInfo EEClassTypeInfo = new Target.TypeInfo() + + private static readonly (string Name, DataType Type)[] EEClassFields = new[] { - Fields = new Dictionary { - { nameof (Data.EEClass.MethodTable), new () { Offset = 8, Type = DataType.pointer}}, - { nameof (Data.EEClass.CorTypeAttr), new () { Offset = 16, Type = DataType.uint32}}, - { nameof (Data.EEClass.NumMethods), new () { Offset = 20, Type = DataType.uint16}}, - { nameof (Data.EEClass.InternalCorElementType), new () { Offset = 22, Type = DataType.uint8}}, - { nameof (Data.EEClass.NumNonVirtualSlots), new () { Offset = 24, Type = DataType.uint16}}, - } + (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), }; - private static readonly Target.TypeInfo ArrayClassTypeInfo = new Target.TypeInfo() { Fields = new Dictionary { @@ -145,7 +143,8 @@ internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary dest = eeClassFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); + targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); return builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) + internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTestHelpers, Target.TypeInfo eeClassTypeInfo, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) { - int size = targetTestHelpers.SizeOfTypeInfo(EEClassTypeInfo) + targetTestHelpers.SizeOfTypeInfo(ArrayClassTypeInfo); + int size = targetTestHelpers.SizeOfTypeInfo(eeClassTypeInfo) + targetTestHelpers.SizeOfTypeInfo(ArrayClassTypeInfo); MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"ArrayClass '{name}'", Address = eeClassPtr, Data = new byte[size] }; Span dest = eeClassFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); - targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); + targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); + targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); targetTestHelpers.Write(dest.Slice(ArrayClassTypeInfo.Fields[nameof(Data.ArrayClass.Rank)].Offset), rank); return builder.AddHeapFragment(eeClassFragment); } @@ -364,7 +363,7 @@ internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetT string name = string.Join(',', array); - builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, builder, arrayClassAddress, name, methodTableAddress, + builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, types[DataType.EEClass], builder, arrayClassAddress, name, methodTableAddress, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); builder = RuntimeTypeSystem.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.cs index 0439d3cceced8..0c3c086f56603 100644 --- a/src/native/managed/cdacreader/tests/MockMemorySpace.cs +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.cs @@ -265,11 +265,11 @@ public bool TryCreateTarget([NotNullWhen(true)] out ContractDescriptorTarget? ta } // Get an allocator for a range of addresses to simplify creating heap fragments - public BumpAllocator CreateAllocator(ulong start, ulong end) + public BumpAllocator CreateAllocator(ulong start, ulong end, int minAlign = 16) { if (_created) throw new InvalidOperationException("Context already created"); - BumpAllocator allocator = new BumpAllocator(start, end); + BumpAllocator allocator = new BumpAllocator(start, end) { MinAlign = minAlign }; foreach (var a in _allocators) { if (allocator.Overlaps(a)) From dbd0f5f968fd66b37e2be2718cc6f75beceb9328 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 16:17:39 -0400 Subject: [PATCH 04/20] add TargetTestHelpers.ExtendLayout --- .../cdacreader/tests/TargetTestHelpers.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs index 68521b1cfab0f..71844fff6f429 100644 --- a/src/native/managed/cdacreader/tests/TargetTestHelpers.cs +++ b/src/native/managed/cdacreader/tests/TargetTestHelpers.cs @@ -341,9 +341,14 @@ public LayoutResult LayoutFields((string Name, DataType Type)[] fields) // Layout the fields of a structure according to the specified layout style. public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type)[] fields) { - Dictionary fieldInfos = new (); - int maxAlign = 1; 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) + { + Dictionary fieldInfos = new (); for (int i = 0; i < fields.Length; i++) { var (name, type) = fields[i]; @@ -373,4 +378,14 @@ public LayoutResult LayoutFields(FieldLayout style, (string Name, DataType Type return new LayoutResult() { Fields = fieldInfos, Stride = (uint)stride, MaxAlign = (uint)maxAlign}; } + // 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(FieldLayout fieldLayout, (string Name, DataType Type)[] fields, LayoutResult baseClass) + { + int offset = (int)baseClass.Stride; + int maxAlign = (int)baseClass.MaxAlign; + return LayoutFieldsWorker(fieldLayout, fields, ref offset, ref maxAlign); + } + } From 55b8f7a2349640af02991252da78c30b8aaa6b80 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 16:18:45 -0400 Subject: [PATCH 05/20] use ExtendLayout for ArrayClass tests --- .../cdacreader/tests/MockDescriptors.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 6f0b74f44428c..a4cf94bb2c64b 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -33,11 +33,10 @@ private static readonly (string Name, DataType Type)[] EEClassFields = new[] (nameof(Data.EEClass.InternalCorElementType), DataType.uint8), (nameof(Data.EEClass.NumNonVirtualSlots), DataType.uint16), }; - private static readonly Target.TypeInfo ArrayClassTypeInfo = new Target.TypeInfo() + + private static readonly (string Name, DataType Type)[] ArrayClassFields = new[] { - Fields = new Dictionary { - { nameof (Data.ArrayClass.Rank), new () { Offset = 0x70, Type = DataType.uint8}}, - } + (nameof(Data.ArrayClass.Rank), DataType.uint8), }; private static readonly Target.TypeInfo ObjectTypeInfo = new() @@ -140,12 +139,13 @@ public static class RuntimeTypeSystem internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary types) { - //TODO(cdac): use targetTestHelpers.LayoutFields() var layout = targetTestHelpers.LayoutFields(MethodTableFields); types[DataType.MethodTable] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; - layout = targetTestHelpers.LayoutFields(EEClassFields); + var eeClassLayout = targetTestHelpers.LayoutFields(EEClassFields); + layout = eeClassLayout; types[DataType.EEClass] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; - types[DataType.ArrayClass] = ArrayClassTypeInfo; + layout = targetTestHelpers.ExtendLayout(ArrayClassFields, eeClassLayout); + types[DataType.ArrayClass] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; } internal static readonly (string Name, ulong Value, string? Type)[] Globals = @@ -180,16 +180,18 @@ internal static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestH return builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTestHelpers, Target.TypeInfo eeClassTypeInfo, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) + internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTestHelpers, Dictionary types, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) { - int size = targetTestHelpers.SizeOfTypeInfo(eeClassTypeInfo) + targetTestHelpers.SizeOfTypeInfo(ArrayClassTypeInfo); + Target.TypeInfo eeClassTypeInfo = types[DataType.EEClass]; + Target.TypeInfo arrayClassTypeInfo = types[DataType.ArrayClass]; + int size = (int)arrayClassTypeInfo.Size.Value; MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"ArrayClass '{name}'", Address = eeClassPtr, Data = new byte[size] }; Span dest = eeClassFragment.Data; targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); - targetTestHelpers.Write(dest.Slice(ArrayClassTypeInfo.Fields[nameof(Data.ArrayClass.Rank)].Offset), rank); + targetTestHelpers.Write(dest.Slice(arrayClassTypeInfo.Fields[nameof(Data.ArrayClass.Rank)].Offset), rank); return builder.AddHeapFragment(eeClassFragment); } @@ -363,7 +365,7 @@ internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetT string name = string.Join(',', array); - builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, types[DataType.EEClass], builder, arrayClassAddress, name, methodTableAddress, + builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, types, builder, arrayClassAddress, name, methodTableAddress, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); builder = RuntimeTypeSystem.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, From e82278481b4af102ee7344b7906fa45322e195af Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Thu, 24 Oct 2024 16:34:29 -0400 Subject: [PATCH 06/20] remove TargetTestHelpers arg from methods that take a MockMemorySpace.Builder arg can get the helpers from the builder instead --- .../cdacreader/tests/MethodTableTests.cs | 44 +++++++++---------- .../cdacreader/tests/MockDescriptors.cs | 37 +++++++++------- .../managed/cdacreader/tests/ObjectTests.cs | 8 ++-- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 127ca26cfa0cb..03d5619c6dd86 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -27,7 +27,7 @@ private static void RTSContractHelper(MockTarget.Architecture arch, ConfigureCon .SetTypes (rtsTypes) .SetGlobals (MockRTS.Globals); - builder = MockRTS.AddGlobalPointers(targetTestHelpers, rtsTypes[DataType.MethodTable], builder); + builder = MockRTS.AddGlobalPointers(rtsTypes[DataType.MethodTable], builder); if (configure != null) { @@ -54,13 +54,14 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) }); } - private static MockMemorySpace.Builder AddSystemObject(TargetTestHelpers targetTestHelpers, Dictionary types, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) + private static MockMemorySpace.Builder AddSystemObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numMethods = 8; // System.Object has 8 methods const int numVirtuals = 3; // System.Object has 3 virtual methods - builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, + builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); return builder; @@ -74,11 +75,10 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); - TargetTestHelpers targetTestHelpers = new(arch); RTSContractHelper(arch, (types, builder) => { - builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); return builder; }, (target) => @@ -104,19 +104,18 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) const ulong SystemStringEEClassAddress = 0x00000000_7c0020d0; TargetPointer systemStringMethodTablePtr = new TargetPointer(SystemStringMethodTableAddress); TargetPointer systemStringEEClassPtr = new TargetPointer(SystemStringEEClassAddress); - TargetTestHelpers targetTestHelpers = new(arch); RTSContractHelper(arch, (types, builder) => { - builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed; const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; - builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, - mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize, + builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, + mtflags: mtflags, mtflags2: default, baseSize: builder.TargetTestHelpers.StringBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); return builder; }, @@ -135,7 +134,6 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) { - TargetTestHelpers targetTestHelpers = new(arch); const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); @@ -148,8 +146,8 @@ public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) RTSContractHelper(arch, (types, builder) => { - builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); + builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: builder.TargetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); return builder; }, (target) => @@ -183,20 +181,20 @@ public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) RTSContractHelper(arch, (types, builder) => { - builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numInterfaces = 0; const int numVirtuals = 3; const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst - builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, + builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); const uint ginst_mtflags = 0x00000010; // TODO: GenericsMask_GenericInst TargetPointer ginstCanonMT = new TargetPointer(genericDefinitionMethodTablePtr.Value | (ulong)1); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, mtflags: ginst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: genericDefinitionMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); @@ -239,21 +237,21 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) RTSContractHelper(arch, (types, builder) => { - builder = AddSystemObject(targetTestHelpers, types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); const ushort systemArrayNumInterfaces = 4; const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); - builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, + builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); - builder = MockRTS.AddEEClass(targetTestHelpers, types[DataType.EEClass], builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, + builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index a4cf94bb2c64b..c0af85492fd71 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -154,13 +154,14 @@ internal static readonly (string Name, ulong Value, string? Type)[] Globals = (nameof(Constants.Globals.MethodDescAlignment), 8, nameof(DataType.uint64)), ]; - internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + internal static MockMemorySpace.Builder AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { - return AddFreeObjectMethodTable(targetTestHelpers, methodTableTypeInfo, builder); + return AddFreeObjectMethodTable(methodTableTypeInfo, builder); } - private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + private static MockMemorySpace.Builder AddFreeObjectMethodTable(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of Free Object Method Table", Address = TestFreeObjectMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(globalAddr.Data, TestFreeObjectMethodTableAddress); return builder.AddHeapFragments([ @@ -169,8 +170,9 @@ private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelper ]); } - internal static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestHelpers, Target.TypeInfo eeClassTypeInfo, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots) + internal static MockMemorySpace.Builder AddEEClass(Target.TypeInfo eeClassTypeInfo, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(eeClassTypeInfo)] }; Span dest = eeClassFragment.Data; targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); @@ -180,8 +182,9 @@ internal static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestH return builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTestHelpers, Dictionary types, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) + internal static MockMemorySpace.Builder AddArrayClass(Dictionary types, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo eeClassTypeInfo = types[DataType.EEClass]; Target.TypeInfo arrayClassTypeInfo = types[DataType.ArrayClass]; int size = (int)arrayClassTypeInfo.Size.Value; @@ -195,9 +198,10 @@ internal static MockMemorySpace.Builder AddArrayClass(TargetTestHelpers targetTe return builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddMethodTable(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder, TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, + internal static MockMemorySpace.Builder AddMethodTable(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder, TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, TargetPointer module, TargetPointer parentMethodTable, ushort numInterfaces, ushort numVirtuals) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment methodTableFragment = new() { Name = $"MethodTable '{name}'", Address = methodTablePtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] }; Span dest = methodTableFragment.Data; targetTestHelpers.WritePointer(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset), eeClassOrCanonMT); @@ -249,16 +253,17 @@ internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHel (nameof(Constants.Globals.SyncBlockValueToObjectOffset), TestSyncBlockValueToObjectOffset, "uint16"), ]).ToArray(); - internal static MockMemorySpace.Builder AddGlobalPointers(TargetTestHelpers targetTestHelpers, Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + internal static MockMemorySpace.Builder AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { - builder = RuntimeTypeSystem.AddGlobalPointers(targetTestHelpers, methodTableTypeInfo, builder); - builder = AddStringMethodTablePointer(targetTestHelpers, builder); - builder = AddSyncTableEntriesPointer(targetTestHelpers, builder); + builder = RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, builder); + builder = AddStringMethodTablePointer(builder); + builder = AddSyncTableEntriesPointer(builder); return builder; } - private static MockMemorySpace.Builder AddStringMethodTablePointer(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + private static MockMemorySpace.Builder AddStringMethodTablePointer(MockMemorySpace.Builder builder) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of String Method Table", Address = TestStringMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestStringMethodTableAddress); return builder.AddHeapFragments([ @@ -267,8 +272,9 @@ private static MockMemorySpace.Builder AddStringMethodTablePointer(TargetTestHel ]); } - private static MockMemorySpace.Builder AddSyncTableEntriesPointer(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder) + private static MockMemorySpace.Builder AddSyncTableEntriesPointer(MockMemorySpace.Builder builder) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of Sync Table Entries", Address = TestSyncTableEntriesGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestSyncTableEntriesAddress); return builder.AddHeapFragment(fragment); @@ -343,8 +349,9 @@ internal static MockMemorySpace.Builder AddStringObject(TargetTestHelpers target return builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, Array array) + internal static MockMemorySpace.Builder AddArrayObject(MockMemorySpace.Builder builder, TargetPointer address, Array array) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; bool isSingleDimensionZeroLowerBound = array.Rank == 1 && array.GetLowerBound(0) == 0; // Bounds are part of the array object for non-single dimension or non-zero lower bound arrays @@ -365,9 +372,9 @@ internal static MockMemorySpace.Builder AddArrayObject(TargetTestHelpers targetT string name = string.Join(',', array); - builder = RuntimeTypeSystem.AddArrayClass(targetTestHelpers, types, builder, arrayClassAddress, name, methodTableAddress, + builder = RuntimeTypeSystem.AddArrayClass(types, builder, arrayClassAddress, name, methodTableAddress, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); - builder = RuntimeTypeSystem.AddMethodTable(targetTestHelpers, types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, + builder = RuntimeTypeSystem.AddMethodTable(types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0); diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 7ffd305220e12..077888404d1fa 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -24,7 +24,7 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Configure .SetGlobals(MockObject.Globals(targetTestHelpers)) .SetTypes(types); - builder = MockObject.AddGlobalPointers(targetTestHelpers, types[DataType.MethodTable], builder); + builder = MockObject.AddGlobalPointers(types[DataType.MethodTable], builder); if (configure != null) { @@ -95,9 +95,9 @@ public void ArrayData(MockTarget.Architecture arch) ObjectContractHelper(arch, (builder) => { - builder = MockObject.AddArrayObject(targetTestHelpers, builder, SingleDimensionArrayAddress, singleDimension); - builder = MockObject.AddArrayObject(targetTestHelpers, builder, MultiDimensionArrayAddress, multiDimension); - builder = MockObject.AddArrayObject(targetTestHelpers, builder, NonZeroLowerBoundArrayAddress, nonZeroLowerBound); + builder = MockObject.AddArrayObject(builder, SingleDimensionArrayAddress, singleDimension); + builder = MockObject.AddArrayObject(builder, MultiDimensionArrayAddress, multiDimension); + builder = MockObject.AddArrayObject(builder, NonZeroLowerBoundArrayAddress, nonZeroLowerBound); return builder; }, (target) => From 4d766f4f307641ba007950fcc43841612e953471 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 10:47:55 -0400 Subject: [PATCH 07/20] layoutfields for managed objects --- .../cdacreader/tests/MockDescriptors.cs | 68 ++++++++++--------- .../managed/cdacreader/tests/ObjectTests.cs | 30 ++++---- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index c0af85492fd71..9851f2f3d2094 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -39,26 +40,20 @@ private static readonly (string Name, DataType Type)[] ArrayClassFields = new[] (nameof(Data.ArrayClass.Rank), DataType.uint8), }; - private static readonly Target.TypeInfo ObjectTypeInfo = new() + private static readonly (string Name, DataType Type)[] ObjectFields = new[] { - Fields = new Dictionary { - { "m_pMethTab", new() { Offset = 0, Type = DataType.pointer} }, - } + ("m_pMethTab", DataType.pointer), }; - private static readonly Target.TypeInfo StringTypeInfo = new Target.TypeInfo() + private static readonly (string Name, DataType Type)[] StringFields = new[] { - Fields = new Dictionary { - { "m_StringLength", new() { Offset = 0x8, Type = DataType.uint32} }, - { "m_FirstChar", new() { Offset = 0xc, Type = DataType.uint16} }, - } + ("m_StringLength", DataType.uint32), + ("m_FirstChar", DataType.uint16), }; - private static readonly Target.TypeInfo ArrayTypeInfo = new Target.TypeInfo() + private static readonly (string Name, DataType Type)[] ArrayFields = new[] { - Fields = new Dictionary { - { "m_NumComponents", new() { Offset = 0x8, Type = DataType.uint32} }, - }, + ("m_NumComponents", DataType.uint32), }; private static readonly Target.TypeInfo SyncTableEntryInfo = new Target.TypeInfo() @@ -234,9 +229,13 @@ public static class Object { Dictionary types = new(); RuntimeTypeSystem.AddTypes(helpers, types); - types[DataType.Object] = ObjectTypeInfo; - types[DataType.String] = StringTypeInfo; - types[DataType.Array] = ArrayTypeInfo with { Size = helpers.ArrayBaseSize }; + var objectLayout = helpers.LayoutFields(ObjectFields); + types[DataType.Object] = new Target.TypeInfo() { Fields = objectLayout.Fields, Size = objectLayout.Stride }; + var layout = helpers.ExtendLayout(StringFields, objectLayout); + types[DataType.String] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + layout = helpers.ExtendLayout(ArrayFields, objectLayout); + types[DataType.Array] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + Debug.Assert(types[DataType.Array].Size == helpers.ArrayBaseSize); types[DataType.SyncTableEntry] = SyncTableEntryInfo with { Size = (uint)helpers.SizeOfTypeInfo(SyncTableEntryInfo) }; types[DataType.SyncBlock] = SyncBlockTypeInfo; types[DataType.InteropSyncBlockInfo] = InteropSyncBlockTypeInfo; @@ -280,22 +279,25 @@ private static MockMemorySpace.Builder AddSyncTableEntriesPointer(MockMemorySpac return builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable) + internal static MockMemorySpace.Builder AddObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable) { - MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(ObjectTypeInfo)] }; + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; + Target.TypeInfo objectTypeInfo = types[DataType.Object]; + MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(objectTypeInfo)] }; Span dest = fragment.Data; - targetTestHelpers.WritePointer(dest.Slice(ObjectTypeInfo.Fields["m_pMethTab"].Offset), methodTable); + targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTable); return builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddObjectWithSyncBlock(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) + internal static MockMemorySpace.Builder AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; const uint IsSyncBlockIndexBits = 0x08000000; const uint SyncBlockIndexMask = (1 << 26) - 1; if ((syncBlockIndex & SyncBlockIndexMask) != syncBlockIndex) throw new ArgumentOutOfRangeException(nameof(syncBlockIndex), "Invalid sync block index"); - builder = AddObject(targetTestHelpers, builder, address, methodTable); + builder = AddObject(types, builder, address, methodTable); // Add the sync table value before the object uint syncTableValue = IsSyncBlockIndexBits | syncBlockIndex; @@ -338,18 +340,21 @@ private static MockMemorySpace.Builder AddSyncBlock(TargetTestHelpers targetTest return builder.AddHeapFragments([syncTableEntry, syncBlock, interopInfo]); } - internal static MockMemorySpace.Builder AddStringObject(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer address, string value) + internal static MockMemorySpace.Builder AddStringObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, string value) { - int size = targetTestHelpers.SizeOfTypeInfo(ObjectTypeInfo) + targetTestHelpers.SizeOfTypeInfo(StringTypeInfo) + value.Length * sizeof(char); + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; + Target.TypeInfo objectTypeInfo = types[DataType.Object]; + Target.TypeInfo stringTypeInfo = types[DataType.String]; + int size = (int)stringTypeInfo.Size.Value + value.Length * sizeof(char); MockMemorySpace.HeapFragment fragment = new() { Name = $"String = '{value}'", Address = address, Data = new byte[size] }; Span dest = fragment.Data; - targetTestHelpers.WritePointer(dest.Slice(ObjectTypeInfo.Fields["m_pMethTab"].Offset), TestStringMethodTableAddress); - targetTestHelpers.Write(dest.Slice(StringTypeInfo.Fields["m_StringLength"].Offset), (uint)value.Length); - MemoryMarshal.Cast(value).CopyTo(dest.Slice(StringTypeInfo.Fields["m_FirstChar"].Offset)); + targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), TestStringMethodTableAddress); + targetTestHelpers.Write(dest.Slice(stringTypeInfo.Fields["m_StringLength"].Offset), (uint)value.Length); + MemoryMarshal.Cast(value).CopyTo(dest.Slice(stringTypeInfo.Fields["m_FirstChar"].Offset)); return builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddArrayObject(MockMemorySpace.Builder builder, TargetPointer address, Array array) + internal static MockMemorySpace.Builder AddArrayObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, Array array) { TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; bool isSingleDimensionZeroLowerBound = array.Rank == 1 && array.GetLowerBound(0) == 0; @@ -358,12 +363,13 @@ internal static MockMemorySpace.Builder AddArrayObject(MockMemorySpace.Builder b // << fields that are part of the array type info >> // int32_t bounds[rank]; // int32_t lowerBounds[rank]; - int size = targetTestHelpers.SizeOfTypeInfo(ObjectTypeInfo) + targetTestHelpers.SizeOfTypeInfo(ArrayTypeInfo); + Target.TypeInfo objectTypeInfo = types[DataType.Object]; + Target.TypeInfo arrayTypeInfo = types[DataType.Array]; + int size = (int)arrayTypeInfo.Size.Value; if (!isSingleDimensionZeroLowerBound) size += array.Rank * sizeof(int) * 2; ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); - Dictionary types = Types(targetTestHelpers); // TODO(cdac): pass in types ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); uint flags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; @@ -380,8 +386,8 @@ internal static MockMemorySpace.Builder AddArrayObject(MockMemorySpace.Builder b MockMemorySpace.HeapFragment fragment = new() { Name = $"Array = '{string.Join(',', array)}'", Address = address, Data = new byte[size] }; Span dest = fragment.Data; - targetTestHelpers.WritePointer(dest.Slice(ObjectTypeInfo.Fields["m_pMethTab"].Offset), methodTableAddress); - targetTestHelpers.Write(dest.Slice(ArrayTypeInfo.Fields["m_NumComponents"].Offset), (uint)array.Length); + targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTableAddress); + targetTestHelpers.Write(dest.Slice(arrayTypeInfo.Fields["m_NumComponents"].Offset), (uint)array.Length); return builder.AddHeapFragment(fragment); } } diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 077888404d1fa..d9c875af11521 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Text; using Xunit; @@ -11,7 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public unsafe class ObjectTests { - private delegate MockMemorySpace.Builder ConfigureContextBuilder(MockMemorySpace.Builder builder); + private delegate MockMemorySpace.Builder ConfigureContextBuilder(Dictionary types, MockMemorySpace.Builder builder); private static void ObjectContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action testCase) { @@ -28,7 +29,7 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Configure if (configure != null) { - builder = configure(builder); + builder = configure(types, builder); } bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target); @@ -42,11 +43,10 @@ public void UnmaskMethodTableAddress(MockTarget.Architecture arch) { const ulong TestObjectAddress = 0x00000000_10000010; const ulong TestMethodTableAddress = 0x00000000_10000027; - TargetTestHelpers targetTestHelpers = new(arch); ObjectContractHelper(arch, - (builder) => + (types, builder) => { - builder = MockObject.AddObject(targetTestHelpers, builder, TestObjectAddress, TestMethodTableAddress); + builder = MockObject.AddObject(types, builder, TestObjectAddress, TestMethodTableAddress); return builder; }, (target) => @@ -64,11 +64,10 @@ public void StringValue(MockTarget.Architecture arch) { const ulong TestStringAddress = 0x00000000_10000010; string expected = "test_string_value"; - TargetTestHelpers targetTestHelpers = new(arch); ObjectContractHelper(arch, - (builder) => + (types, builder) => { - builder = MockObject.AddStringObject(targetTestHelpers, builder, TestStringAddress, expected); + builder = MockObject.AddStringObject(types, builder, TestStringAddress, expected); return builder; }, (target) => @@ -93,11 +92,11 @@ public void ArrayData(MockTarget.Architecture arch) Array nonZeroLowerBound = Array.CreateInstance(typeof(int), [10], [5]); TargetTestHelpers targetTestHelpers = new(arch); ObjectContractHelper(arch, - (builder) => + (types, builder) => { - builder = MockObject.AddArrayObject(builder, SingleDimensionArrayAddress, singleDimension); - builder = MockObject.AddArrayObject(builder, MultiDimensionArrayAddress, multiDimension); - builder = MockObject.AddArrayObject(builder, NonZeroLowerBoundArrayAddress, nonZeroLowerBound); + builder = MockObject.AddArrayObject(types, builder, SingleDimensionArrayAddress, singleDimension); + builder = MockObject.AddArrayObject(types, builder, MultiDimensionArrayAddress, multiDimension); + builder = MockObject.AddArrayObject(types, builder, NonZeroLowerBoundArrayAddress, nonZeroLowerBound); return builder; }, (target) => @@ -138,13 +137,12 @@ public void ComData(MockTarget.Architecture arch) TargetPointer expectedRCW = 0xaaaa; TargetPointer expectedCCW = 0xbbbb; - TargetTestHelpers targetTestHelpers = new(arch); ObjectContractHelper(arch, - (builder) => + (types, builder) => { uint syncBlockIndex = 0; - builder = MockObject.AddObjectWithSyncBlock(targetTestHelpers, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); - builder = MockObject.AddObjectWithSyncBlock(targetTestHelpers, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); + builder = MockObject.AddObjectWithSyncBlock(types, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); + builder = MockObject.AddObjectWithSyncBlock(types, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); return builder; }, (target) => From 3b93b5748da3832893adefc686b68d292eb2c229 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 11:10:21 -0400 Subject: [PATCH 08/20] use LayoutFields for sync blocks --- .../cdacreader/tests/MockDescriptors.cs | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 9851f2f3d2094..efa4ef2709b55 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -56,26 +56,20 @@ private static readonly (string Name, DataType Type)[] ArrayFields = new[] ("m_NumComponents", DataType.uint32), }; - private static readonly Target.TypeInfo SyncTableEntryInfo = new Target.TypeInfo() + private static readonly (string Name, DataType Type)[] SyncTableEntryFields = new[] { - Fields = new Dictionary { - { nameof(Data.SyncTableEntry.SyncBlock), new() { Offset = 0, Type = DataType.pointer} }, - }, + (nameof(Data.SyncTableEntry.SyncBlock), DataType.pointer), }; - private static readonly Target.TypeInfo SyncBlockTypeInfo = new Target.TypeInfo() + private static readonly (string Name, DataType Type)[] SyncBlockFields = new[] { - Fields = new Dictionary { - { nameof(Data.SyncBlock.InteropInfo), new() { Offset = 0, Type = DataType.pointer} }, - }, + (nameof(Data.SyncBlock.InteropInfo), DataType.pointer), }; - private static readonly Target.TypeInfo InteropSyncBlockTypeInfo = new Target.TypeInfo() + private static readonly (string Name, DataType Type)[] InteropSyncBlockFields = new[] { - Fields = new Dictionary { - { nameof(Data.InteropSyncBlockInfo.RCW), new() { Offset = 0, Type = DataType.pointer} }, - { nameof(Data.InteropSyncBlockInfo.CCW), new() { Offset = 0x8, Type = DataType.pointer} }, - }, + (nameof(Data.InteropSyncBlockInfo.RCW), DataType.pointer), + (nameof(Data.InteropSyncBlockInfo.CCW), DataType.pointer), }; private static readonly (string, DataType)[] ModuleFields = @@ -236,9 +230,12 @@ public static class Object layout = helpers.ExtendLayout(ArrayFields, objectLayout); types[DataType.Array] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; Debug.Assert(types[DataType.Array].Size == helpers.ArrayBaseSize); - types[DataType.SyncTableEntry] = SyncTableEntryInfo with { Size = (uint)helpers.SizeOfTypeInfo(SyncTableEntryInfo) }; - types[DataType.SyncBlock] = SyncBlockTypeInfo; - types[DataType.InteropSyncBlockInfo] = InteropSyncBlockTypeInfo; + layout = helpers.LayoutFields(SyncTableEntryFields); + types[DataType.SyncTableEntry] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + layout = helpers.LayoutFields(SyncBlockFields); + types[DataType.SyncBlock] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + layout = helpers.LayoutFields(InteropSyncBlockFields); + types[DataType.InteropSyncBlockInfo] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; return types; } @@ -307,35 +304,39 @@ internal static MockMemorySpace.Builder AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw) { + TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; // Tests write the sync blocks starting at TestSyncBlocksAddress const ulong TestSyncBlocksAddress = 0x00000000_e0000000; - int syncBlockSize = targetTestHelpers.SizeOfTypeInfo(SyncBlockTypeInfo); - int interopSyncBlockInfoSize = targetTestHelpers.SizeOfTypeInfo(InteropSyncBlockTypeInfo); + Target.TypeInfo syncBlockTypeInfo = types[DataType.SyncBlock]; + Target.TypeInfo interopSyncBlockTypeInfo = types[DataType.InteropSyncBlockInfo]; + int syncBlockSize = targetTestHelpers.SizeOfTypeInfo(syncBlockTypeInfo); + int interopSyncBlockInfoSize = targetTestHelpers.SizeOfTypeInfo(interopSyncBlockTypeInfo); ulong syncBlockAddr = TestSyncBlocksAddress + index * (ulong)(syncBlockSize + interopSyncBlockInfoSize); // Add the sync table entry - pointing at the sync block - uint syncTableEntrySize = (uint)targetTestHelpers.SizeOfTypeInfo(SyncTableEntryInfo); + Target.TypeInfo syncTableEntryInfo = types[DataType.SyncTableEntry]; + uint syncTableEntrySize = (uint)targetTestHelpers.SizeOfTypeInfo(syncTableEntryInfo); ulong syncTableEntryAddr = TestSyncTableEntriesAddress + index * syncTableEntrySize; MockMemorySpace.HeapFragment syncTableEntry = new() { Name = $"SyncTableEntries[{index}]", Address = syncTableEntryAddr, Data = new byte[syncTableEntrySize] }; Span syncTableEntryData = syncTableEntry.Data; - targetTestHelpers.WritePointer(syncTableEntryData.Slice(SyncTableEntryInfo.Fields[nameof(Data.SyncTableEntry.SyncBlock)].Offset), syncBlockAddr); + targetTestHelpers.WritePointer(syncTableEntryData.Slice(syncTableEntryInfo.Fields[nameof(Data.SyncTableEntry.SyncBlock)].Offset), syncBlockAddr); // Add the sync block - pointing at the interop sync block info ulong interopInfoAddr = syncBlockAddr + (ulong)syncBlockSize; MockMemorySpace.HeapFragment syncBlock = new() { Name = $"Sync Block", Address = syncBlockAddr, Data = new byte[syncBlockSize] }; Span syncBlockData = syncBlock.Data; - targetTestHelpers.WritePointer(syncBlockData.Slice(SyncBlockTypeInfo.Fields[nameof(Data.SyncBlock.InteropInfo)].Offset), interopInfoAddr); + targetTestHelpers.WritePointer(syncBlockData.Slice(syncBlockTypeInfo.Fields[nameof(Data.SyncBlock.InteropInfo)].Offset), interopInfoAddr); // Add the interop sync block info MockMemorySpace.HeapFragment interopInfo = new() { Name = $"Interop Sync Block Info", Address = interopInfoAddr, Data = new byte[interopSyncBlockInfoSize] }; Span interopInfoData = interopInfo.Data; - targetTestHelpers.WritePointer(interopInfoData.Slice(InteropSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.RCW)].Offset), rcw); - targetTestHelpers.WritePointer(interopInfoData.Slice(InteropSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.CCW)].Offset), ccw); + targetTestHelpers.WritePointer(interopInfoData.Slice(interopSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.RCW)].Offset), rcw); + targetTestHelpers.WritePointer(interopInfoData.Slice(interopSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.CCW)].Offset), ccw); return builder.AddHeapFragments([syncTableEntry, syncBlock, interopInfo]); } From 8080f043891e13914d64adf8656090873eaa2043 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 11:26:05 -0400 Subject: [PATCH 09/20] make MockDescriptors.Object a non-static builder class --- .../cdacreader/tests/MockDescriptors.cs | 61 +++++++++++-------- .../managed/cdacreader/tests/ObjectTests.cs | 25 ++++---- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index efa4ef2709b55..ee19d8fc84e63 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -207,7 +207,7 @@ internal static MockMemorySpace.Builder AddMethodTable(Target.TypeInfo methodTab } } - public static class Object + public class Object { private const ulong TestStringMethodTableGlobalAddress = 0x00000000_100000a0; private const ulong TestStringMethodTableAddress = 0x00000000_100000a8; @@ -219,9 +219,17 @@ public static class Object internal const ulong TestObjectToMethodTableUnmask = 0x7; internal const ulong TestSyncBlockValueToObjectOffset = sizeof(uint); - internal static Dictionary Types(TargetTestHelpers helpers) + internal readonly Dictionary Types; + internal readonly MockMemorySpace.Builder Builder; + + internal Object(Dictionary types, MockMemorySpace.Builder builder) + { + Types = types; + Builder = builder; + } + + internal static void AddTypes(Dictionary types, TargetTestHelpers helpers) { - Dictionary types = new(); RuntimeTypeSystem.AddTypes(helpers, types); var objectLayout = helpers.LayoutFields(ObjectFields); types[DataType.Object] = new Target.TypeInfo() { Fields = objectLayout.Fields, Size = objectLayout.Stride }; @@ -236,7 +244,6 @@ public static class Object types[DataType.SyncBlock] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; layout = helpers.LayoutFields(InteropSyncBlockFields); types[DataType.InteropSyncBlockInfo] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; - return types; } internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHelpers helpers) => RuntimeTypeSystem.Globals.Concat( @@ -249,20 +256,20 @@ internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHel (nameof(Constants.Globals.SyncBlockValueToObjectOffset), TestSyncBlockValueToObjectOffset, "uint16"), ]).ToArray(); - internal static MockMemorySpace.Builder AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + internal static void AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) { - builder = RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, builder); - builder = AddStringMethodTablePointer(builder); - builder = AddSyncTableEntriesPointer(builder); - return builder; + RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, builder); + AddStringMethodTablePointer(builder); + AddSyncTableEntriesPointer(builder); + } - private static MockMemorySpace.Builder AddStringMethodTablePointer(MockMemorySpace.Builder builder) + private static void AddStringMethodTablePointer(MockMemorySpace.Builder builder) { TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of String Method Table", Address = TestStringMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestStringMethodTableAddress); - return builder.AddHeapFragments([ + builder.AddHeapFragments([ fragment, new () { Name = "String Method Table", Address = TestStringMethodTableAddress, Data = new byte[targetTestHelpers.PointerSize] } ]); @@ -276,17 +283,17 @@ private static MockMemorySpace.Builder AddSyncTableEntriesPointer(MockMemorySpac return builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable) + internal static void AddObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable) { TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo objectTypeInfo = types[DataType.Object]; MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(objectTypeInfo)] }; Span dest = fragment.Data; targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTable); - return builder.AddHeapFragment(fragment); + builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) + internal static void AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) { TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; const uint IsSyncBlockIndexBits = 0x08000000; @@ -294,20 +301,20 @@ internal static MockMemorySpace.Builder AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw) + private static void AddSyncBlock(Dictionary types, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw) { TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; // Tests write the sync blocks starting at TestSyncBlocksAddress @@ -338,11 +345,13 @@ private static MockMemorySpace.Builder AddSyncBlock(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, string value) + internal void AddStringObject(TargetPointer address, string value) { + MockMemorySpace.Builder builder = Builder; + Dictionary types = Types; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo objectTypeInfo = types[DataType.Object]; Target.TypeInfo stringTypeInfo = types[DataType.String]; @@ -352,11 +361,13 @@ internal static MockMemorySpace.Builder AddStringObject(Dictionary(value).CopyTo(dest.Slice(stringTypeInfo.Fields["m_FirstChar"].Offset)); - return builder.AddHeapFragment(fragment); + builder.AddHeapFragment(fragment); } - internal static MockMemorySpace.Builder AddArrayObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, Array array) + internal void AddArrayObject(TargetPointer address, Array array) { + MockMemorySpace.Builder builder = Builder; + Dictionary types = Types; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; bool isSingleDimensionZeroLowerBound = array.Rank == 1 && array.GetLowerBound(0) == 0; @@ -379,9 +390,9 @@ internal static MockMemorySpace.Builder AddArrayObject(Dictionary dest = fragment.Data; targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTableAddress); targetTestHelpers.Write(dest.Slice(arrayTypeInfo.Fields["m_NumComponents"].Offset), (uint)array.Length); - return builder.AddHeapFragment(fragment); + builder.AddHeapFragment(fragment); } } diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index d9c875af11521..92c3c94129730 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Text; using Xunit; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -19,13 +18,14 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Configure TargetTestHelpers targetTestHelpers = new(arch); MockMemorySpace.Builder builder = new(targetTestHelpers); - var types = MockObject.Types(targetTestHelpers); + Dictionary types = new(); + MockObject.AddTypes(types, targetTestHelpers); builder = builder .SetContracts([ nameof (Contracts.Object), nameof (Contracts.RuntimeTypeSystem) ]) .SetGlobals(MockObject.Globals(targetTestHelpers)) .SetTypes(types); - builder = MockObject.AddGlobalPointers(types[DataType.MethodTable], builder); + MockObject.AddGlobalPointers(types[DataType.MethodTable], builder); if (configure != null) { @@ -46,7 +46,7 @@ public void UnmaskMethodTableAddress(MockTarget.Architecture arch) ObjectContractHelper(arch, (types, builder) => { - builder = MockObject.AddObject(types, builder, TestObjectAddress, TestMethodTableAddress); + MockObject.AddObject(types, builder, TestObjectAddress, TestMethodTableAddress); return builder; }, (target) => @@ -67,7 +67,8 @@ public void StringValue(MockTarget.Architecture arch) ObjectContractHelper(arch, (types, builder) => { - builder = MockObject.AddStringObject(types, builder, TestStringAddress, expected); + MockObject objectBuilder = new(types, builder); + objectBuilder.AddStringObject(TestStringAddress, expected); return builder; }, (target) => @@ -94,9 +95,10 @@ public void ArrayData(MockTarget.Architecture arch) ObjectContractHelper(arch, (types, builder) => { - builder = MockObject.AddArrayObject(types, builder, SingleDimensionArrayAddress, singleDimension); - builder = MockObject.AddArrayObject(types, builder, MultiDimensionArrayAddress, multiDimension); - builder = MockObject.AddArrayObject(types, builder, NonZeroLowerBoundArrayAddress, nonZeroLowerBound); + MockObject objectBuilder = new(types, builder); + objectBuilder.AddArrayObject(SingleDimensionArrayAddress, singleDimension); + objectBuilder.AddArrayObject(MultiDimensionArrayAddress, multiDimension); + objectBuilder.AddArrayObject(NonZeroLowerBoundArrayAddress, nonZeroLowerBound); return builder; }, (target) => @@ -107,7 +109,8 @@ public void ArrayData(MockTarget.Architecture arch) TargetPointer data = contract.GetArrayData(SingleDimensionArrayAddress, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds); Assert.Equal(SingleDimensionArrayAddress + targetTestHelpers.ArrayBaseBaseSize - targetTestHelpers.ObjHeaderSize, data.Value); Assert.Equal((uint)singleDimension.Length, count); - Assert.Equal(SingleDimensionArrayAddress + (ulong)MockObject.Types(targetTestHelpers)[DataType.Array].Fields["m_NumComponents"].Offset, boundsStart.Value); + Target.TypeInfo arrayType = target.GetTypeInfo(DataType.Array); + Assert.Equal(SingleDimensionArrayAddress + (ulong)arrayType.Fields["m_NumComponents"].Offset, boundsStart.Value); Assert.Equal(MockObject.TestArrayBoundsZeroGlobalAddress, lowerBounds.Value); } { @@ -141,8 +144,8 @@ public void ComData(MockTarget.Architecture arch) (types, builder) => { uint syncBlockIndex = 0; - builder = MockObject.AddObjectWithSyncBlock(types, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); - builder = MockObject.AddObjectWithSyncBlock(types, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); + MockObject.AddObjectWithSyncBlock(types, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); + MockObject.AddObjectWithSyncBlock(types, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); return builder; }, (target) => From 63bc4a5192445ea1f556ec41bed71ab683d4aeba Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 11:31:03 -0400 Subject: [PATCH 10/20] more builder cleanup --- .../cdacreader/tests/MockDescriptors.cs | 12 ++++++----- .../managed/cdacreader/tests/ObjectTests.cs | 20 +++++++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index ee19d8fc84e63..e05a644f2ebb4 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -283,25 +283,27 @@ private static MockMemorySpace.Builder AddSyncTableEntriesPointer(MockMemorySpac return builder.AddHeapFragment(fragment); } - internal static void AddObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable) + internal void AddObject(TargetPointer address, TargetPointer methodTable) { + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; - Target.TypeInfo objectTypeInfo = types[DataType.Object]; + Target.TypeInfo objectTypeInfo = Types[DataType.Object]; MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(objectTypeInfo)] }; Span dest = fragment.Data; targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTable); builder.AddHeapFragment(fragment); } - internal static void AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) + internal void AddObjectWithSyncBlock(TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) { + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; const uint IsSyncBlockIndexBits = 0x08000000; const uint SyncBlockIndexMask = (1 << 26) - 1; if ((syncBlockIndex & SyncBlockIndexMask) != syncBlockIndex) throw new ArgumentOutOfRangeException(nameof(syncBlockIndex), "Invalid sync block index"); - AddObject(types, builder, address, methodTable); + AddObject(address, methodTable); // Add the sync table value before the object uint syncTableValue = IsSyncBlockIndexBits | syncBlockIndex; @@ -311,7 +313,7 @@ internal static void AddObjectWithSyncBlock(Dictionary types, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw) diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 92c3c94129730..008eaa854a260 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -11,9 +11,8 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public unsafe class ObjectTests { - private delegate MockMemorySpace.Builder ConfigureContextBuilder(Dictionary types, MockMemorySpace.Builder builder); - private static void ObjectContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action testCase) + private static void ObjectContractHelper(MockTarget.Architecture arch, Action, MockMemorySpace.Builder> configure, Action testCase) { TargetTestHelpers targetTestHelpers = new(arch); @@ -27,10 +26,7 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Configure MockObject.AddGlobalPointers(types[DataType.MethodTable], builder); - if (configure != null) - { - builder = configure(types, builder); - } + configure?.Invoke(types, builder); bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target); Assert.True(success); @@ -46,8 +42,8 @@ public void UnmaskMethodTableAddress(MockTarget.Architecture arch) ObjectContractHelper(arch, (types, builder) => { - MockObject.AddObject(types, builder, TestObjectAddress, TestMethodTableAddress); - return builder; + MockObject objectBuilder = new(types, builder); + objectBuilder.AddObject(TestObjectAddress, TestMethodTableAddress); }, (target) => { @@ -69,7 +65,6 @@ public void StringValue(MockTarget.Architecture arch) { MockObject objectBuilder = new(types, builder); objectBuilder.AddStringObject(TestStringAddress, expected); - return builder; }, (target) => { @@ -99,7 +94,6 @@ public void ArrayData(MockTarget.Architecture arch) objectBuilder.AddArrayObject(SingleDimensionArrayAddress, singleDimension); objectBuilder.AddArrayObject(MultiDimensionArrayAddress, multiDimension); objectBuilder.AddArrayObject(NonZeroLowerBoundArrayAddress, nonZeroLowerBound); - return builder; }, (target) => { @@ -144,9 +138,9 @@ public void ComData(MockTarget.Architecture arch) (types, builder) => { uint syncBlockIndex = 0; - MockObject.AddObjectWithSyncBlock(types, builder, TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); - MockObject.AddObjectWithSyncBlock(types, builder, TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); - return builder; + MockObject objectBuilder = new(types, builder); + objectBuilder.AddObjectWithSyncBlock(TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); + objectBuilder.AddObjectWithSyncBlock(TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); }, (target) => { From b04386501e53011315f872a2cbad42786c9194e0 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 11:36:01 -0400 Subject: [PATCH 11/20] more builder instances --- .../cdacreader/tests/MockDescriptors.cs | 15 +++++++++------ .../managed/cdacreader/tests/ObjectTests.cs | 19 ++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index e05a644f2ebb4..e9e897d3bef42 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -256,16 +256,18 @@ internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHel (nameof(Constants.Globals.SyncBlockValueToObjectOffset), TestSyncBlockValueToObjectOffset, "uint16"), ]).ToArray(); - internal static void AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + internal void AddGlobalPointers() { - RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, builder); - AddStringMethodTablePointer(builder); - AddSyncTableEntriesPointer(builder); + Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; + RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, Builder); + AddStringMethodTablePointer(); + AddSyncTableEntriesPointer(); } - private static void AddStringMethodTablePointer(MockMemorySpace.Builder builder) + private void AddStringMethodTablePointer() { + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of String Method Table", Address = TestStringMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestStringMethodTableAddress); @@ -275,8 +277,9 @@ private static void AddStringMethodTablePointer(MockMemorySpace.Builder builder) ]); } - private static MockMemorySpace.Builder AddSyncTableEntriesPointer(MockMemorySpace.Builder builder) + private MockMemorySpace.Builder AddSyncTableEntriesPointer() { + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of Sync Table Entries", Address = TestSyncTableEntriesGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestSyncTableEntriesAddress); diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 008eaa854a260..fbf39447fe1dd 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -12,21 +12,22 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public unsafe class ObjectTests { - private static void ObjectContractHelper(MockTarget.Architecture arch, Action, MockMemorySpace.Builder> configure, Action testCase) + private static void ObjectContractHelper(MockTarget.Architecture arch, Action configure, Action testCase) { TargetTestHelpers targetTestHelpers = new(arch); MockMemorySpace.Builder builder = new(targetTestHelpers); Dictionary types = new(); + MockObject objectBuilder = new(types, builder); MockObject.AddTypes(types, targetTestHelpers); builder = builder .SetContracts([ nameof (Contracts.Object), nameof (Contracts.RuntimeTypeSystem) ]) .SetGlobals(MockObject.Globals(targetTestHelpers)) .SetTypes(types); - MockObject.AddGlobalPointers(types[DataType.MethodTable], builder); + objectBuilder.AddGlobalPointers(); - configure?.Invoke(types, builder); + configure?.Invoke(objectBuilder); bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target); Assert.True(success); @@ -40,9 +41,8 @@ public void UnmaskMethodTableAddress(MockTarget.Architecture arch) const ulong TestObjectAddress = 0x00000000_10000010; const ulong TestMethodTableAddress = 0x00000000_10000027; ObjectContractHelper(arch, - (types, builder) => + (objectBuilder) => { - MockObject objectBuilder = new(types, builder); objectBuilder.AddObject(TestObjectAddress, TestMethodTableAddress); }, (target) => @@ -61,9 +61,8 @@ public void StringValue(MockTarget.Architecture arch) const ulong TestStringAddress = 0x00000000_10000010; string expected = "test_string_value"; ObjectContractHelper(arch, - (types, builder) => + (objectBuilder) => { - MockObject objectBuilder = new(types, builder); objectBuilder.AddStringObject(TestStringAddress, expected); }, (target) => @@ -88,9 +87,8 @@ public void ArrayData(MockTarget.Architecture arch) Array nonZeroLowerBound = Array.CreateInstance(typeof(int), [10], [5]); TargetTestHelpers targetTestHelpers = new(arch); ObjectContractHelper(arch, - (types, builder) => + (objectBuilder) => { - MockObject objectBuilder = new(types, builder); objectBuilder.AddArrayObject(SingleDimensionArrayAddress, singleDimension); objectBuilder.AddArrayObject(MultiDimensionArrayAddress, multiDimension); objectBuilder.AddArrayObject(NonZeroLowerBoundArrayAddress, nonZeroLowerBound); @@ -135,10 +133,9 @@ public void ComData(MockTarget.Architecture arch) TargetPointer expectedCCW = 0xbbbb; ObjectContractHelper(arch, - (types, builder) => + (objectBuilder) => { uint syncBlockIndex = 0; - MockObject objectBuilder = new(types, builder); objectBuilder.AddObjectWithSyncBlock(TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); objectBuilder.AddObjectWithSyncBlock(TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); }, From 1816fc6a9501cfd8836827cbe81086702768918c Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 11:58:48 -0400 Subject: [PATCH 12/20] more MockDescriptors refactoring more instance methods --- .../cdacreader/tests/MethodTableTests.cs | 72 ++++++++----------- .../cdacreader/tests/MockDescriptors.cs | 58 +++++++++------ .../managed/cdacreader/tests/ObjectTests.cs | 4 +- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 03d5619c6dd86..8ad1adabff2b3 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -12,27 +12,22 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests; public class MethodTableTests { - // a delegate for adding more heap fragments to the context builder - private delegate MockMemorySpace.Builder ConfigureContextBuilder(Dictionary types, MockMemorySpace.Builder builder); - - private static void RTSContractHelper(MockTarget.Architecture arch, ConfigureContextBuilder configure, Action testCase) + private static void RTSContractHelper(MockTarget.Architecture arch, Action configure, Action testCase) { TargetTestHelpers targetTestHelpers = new(arch); MockMemorySpace.Builder builder = new(targetTestHelpers); Dictionary rtsTypes = new (); + MockRTS rtsBuilder = new (rtsTypes, builder); MockRTS.AddTypes(targetTestHelpers, rtsTypes); - builder = builder + builder .SetContracts ([ nameof(Contracts.RuntimeTypeSystem) ]) .SetTypes (rtsTypes) .SetGlobals (MockRTS.Globals); - builder = MockRTS.AddGlobalPointers(rtsTypes[DataType.MethodTable], builder); + rtsBuilder.AddGlobalPointers(); - if (configure != null) - { - builder = configure(rtsTypes, builder); - } + configure?.Invoke(rtsBuilder); bool success = builder.TryCreateTarget(out ContractDescriptorTarget? target); Assert.True(success); @@ -54,17 +49,18 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) }); } - private static MockMemorySpace.Builder AddSystemObject(Dictionary types, MockMemorySpace.Builder builder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) + private void AddSystemObject(MockRTS rtsBuilder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) { + MockMemorySpace.Builder builder = rtsBuilder.Builder; + Dictionary types = rtsBuilder.Types; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numMethods = 8; // System.Object has 8 methods const int numVirtuals = 3; // System.Object has 3 virtual methods - builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, + rtsBuilder.AddEEClass(systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + rtsBuilder.AddMethodTable(systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); - return builder; } [Theory] @@ -76,10 +72,9 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); RTSContractHelper(arch, - (types, builder) => + (rtsBuilder) => { - builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); - return builder; + AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); }, (target) => { @@ -105,19 +100,18 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) TargetPointer systemStringMethodTablePtr = new TargetPointer(SystemStringMethodTableAddress); TargetPointer systemStringEEClassPtr = new TargetPointer(SystemStringEEClassAddress); RTSContractHelper(arch, - (types, builder) => + (rtsBuilder) => { - builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed; const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; - builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, - mtflags: mtflags, mtflags2: default, baseSize: builder.TargetTestHelpers.StringBaseSize, + rtsBuilder.AddEEClass(systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + rtsBuilder.AddMethodTable(systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, + mtflags: mtflags, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.StringBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); - return builder; }, (target) => { @@ -144,11 +138,10 @@ public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) TargetPointer badMethodTablePtr = new TargetPointer(badMethodTableAddress); TargetPointer badMethodTableEEClassPtr = new TargetPointer(badMethodTableEEClassAddress); RTSContractHelper(arch, - (types, builder) => + (rtsBuilder) => { - builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: builder.TargetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); - return builder; + AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + rtsBuilder.AddMethodTable(badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); }, (target) => { @@ -179,26 +172,25 @@ public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) const int numMethods = 17; RTSContractHelper(arch, - (types, builder) => + (rtsBuilder) => { - builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numInterfaces = 0; const int numVirtuals = 3; const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst - builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, + rtsBuilder.AddEEClass(genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + rtsBuilder.AddMethodTable(genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); const uint ginst_mtflags = 0x00000010; // TODO: GenericsMask_GenericInst TargetPointer ginstCanonMT = new TargetPointer(genericDefinitionMethodTablePtr.Value | (ulong)1); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, + rtsBuilder.AddMethodTable(genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, mtflags: ginst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: genericDefinitionMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); - return builder; }, (target) => { @@ -235,27 +227,25 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) const uint arrayInstanceComponentSize = 392; RTSContractHelper(arch, - (types, builder) => + (rtsBuilder) => { - builder = AddSystemObject(types, builder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); const ushort systemArrayNumInterfaces = 4; const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); - builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, + rtsBuilder.AddEEClass(systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + rtsBuilder.AddMethodTable(systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); - builder = MockRTS.AddEEClass(types[DataType.EEClass], builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - builder = MockRTS.AddMethodTable(types[DataType.MethodTable], builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, + rtsBuilder.AddEEClass(arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + rtsBuilder.AddMethodTable(arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); - - return builder; }, (target) => { diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index e9e897d3bef42..f1558a4ae8cd4 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -143,24 +143,37 @@ internal static readonly (string Name, ulong Value, string? Type)[] Globals = (nameof(Constants.Globals.MethodDescAlignment), 8, nameof(DataType.uint64)), ]; - internal static MockMemorySpace.Builder AddGlobalPointers(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + internal readonly MockMemorySpace.Builder Builder; + internal readonly Dictionary Types; + + internal RuntimeTypeSystem(Dictionary types, MockMemorySpace.Builder builder) + { + Types = types; + Builder = builder; + } + + internal void AddGlobalPointers() { - return AddFreeObjectMethodTable(methodTableTypeInfo, builder); + AddFreeObjectMethodTable(); } - private static MockMemorySpace.Builder AddFreeObjectMethodTable(Target.TypeInfo methodTableTypeInfo, MockMemorySpace.Builder builder) + private void AddFreeObjectMethodTable() { + MockMemorySpace.Builder builder = Builder; + Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of Free Object Method Table", Address = TestFreeObjectMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(globalAddr.Data, TestFreeObjectMethodTableAddress); - return builder.AddHeapFragments([ + builder.AddHeapFragments([ globalAddr, new () { Name = "Free Object Method Table", Address = TestFreeObjectMethodTableAddress, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] } ]); } - internal static MockMemorySpace.Builder AddEEClass(Target.TypeInfo eeClassTypeInfo, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots) + internal void AddEEClass(TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots) { + Target.TypeInfo eeClassTypeInfo = Types[DataType.EEClass]; + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(eeClassTypeInfo)] }; Span dest = eeClassFragment.Data; @@ -168,11 +181,13 @@ internal static MockMemorySpace.Builder AddEEClass(Target.TypeInfo eeClassTypeIn targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); - return builder.AddHeapFragment(eeClassFragment); + builder.AddHeapFragment(eeClassFragment); } - internal static MockMemorySpace.Builder AddArrayClass(Dictionary types, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) + internal void AddArrayClass(TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) { + Dictionary types = Types; + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo eeClassTypeInfo = types[DataType.EEClass]; Target.TypeInfo arrayClassTypeInfo = types[DataType.ArrayClass]; @@ -184,12 +199,14 @@ internal static MockMemorySpace.Builder AddArrayClass(Dictionary dest = methodTableFragment.Data; @@ -203,7 +220,7 @@ internal static MockMemorySpace.Builder AddMethodTable(Target.TypeInfo methodTab targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.NumVirtuals)].Offset), numVirtuals); // TODO fill in the rest of the fields - return builder.AddHeapFragment(methodTableFragment); + builder.AddHeapFragment(methodTableFragment); } } @@ -219,13 +236,13 @@ public class Object internal const ulong TestObjectToMethodTableUnmask = 0x7; internal const ulong TestSyncBlockValueToObjectOffset = sizeof(uint); - internal readonly Dictionary Types; - internal readonly MockMemorySpace.Builder Builder; + internal readonly RuntimeTypeSystem RTSBuilder; + internal Dictionary Types => RTSBuilder.Types; + internal MockMemorySpace.Builder Builder => RTSBuilder.Builder; - internal Object(Dictionary types, MockMemorySpace.Builder builder) + internal Object(RuntimeTypeSystem rtsBuilder) { - Types = types; - Builder = builder; + RTSBuilder = rtsBuilder; } internal static void AddTypes(Dictionary types, TargetTestHelpers helpers) @@ -258,8 +275,7 @@ internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHel internal void AddGlobalPointers() { - Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; - RuntimeTypeSystem.AddGlobalPointers(methodTableTypeInfo, Builder); + RTSBuilder.AddGlobalPointers(); AddStringMethodTablePointer(); AddSyncTableEntriesPointer(); @@ -277,13 +293,13 @@ private void AddStringMethodTablePointer() ]); } - private MockMemorySpace.Builder AddSyncTableEntriesPointer() + private void AddSyncTableEntriesPointer() { MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of Sync Table Entries", Address = TestSyncTableEntriesGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; targetTestHelpers.WritePointer(fragment.Data, TestSyncTableEntriesAddress); - return builder.AddHeapFragment(fragment); + builder.AddHeapFragment(fragment); } internal void AddObject(TargetPointer address, TargetPointer methodTable) @@ -395,9 +411,9 @@ internal void AddArrayObject(TargetPointer address, Array array) string name = string.Join(',', array); - RuntimeTypeSystem.AddArrayClass(types, builder, arrayClassAddress, name, methodTableAddress, + RTSBuilder.AddArrayClass(arrayClassAddress, name, methodTableAddress, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); - RuntimeTypeSystem.AddMethodTable(types[DataType.MethodTable], builder, methodTableAddress, name, arrayClassAddress, + RTSBuilder.AddMethodTable(methodTableAddress, name, arrayClassAddress, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0); diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index fbf39447fe1dd..bbf9019fb7a2e 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Diagnostics.DataContractReader.Contracts; using Xunit; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -18,7 +19,8 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Action types = new(); - MockObject objectBuilder = new(types, builder); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(types, builder); + MockObject objectBuilder = new(rtsBuilder); MockObject.AddTypes(types, targetTestHelpers); builder = builder .SetContracts([ nameof (Contracts.Object), nameof (Contracts.RuntimeTypeSystem) ]) From 2ecf688efa09543a813ce08b3b0d013dc67028c4 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 14:32:42 -0400 Subject: [PATCH 13/20] use a bump allocator for RuntimeTypeSystem --- .../cdacreader/tests/MethodTableTests.cs | 114 +++++++----------- .../cdacreader/tests/MockDescriptors.cs | 56 ++++++--- .../managed/cdacreader/tests/ObjectTests.cs | 5 +- 3 files changed, 89 insertions(+), 86 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 8ad1adabff2b3..50e67a19cf35c 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Data; using Xunit; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -18,7 +19,10 @@ private static void RTSContractHelper(MockTarget.Architecture arch, Action rtsTypes = new (); - MockRTS rtsBuilder = new (rtsTypes, builder); + MockRTS rtsBuilder = new (rtsTypes, builder) { + // arbitrary address range + TypeSystemAllocator = builder.CreateAllocator(start: 0x00000000_33000000, end: 0x00000000_34000000), + }; MockRTS.AddTypes(targetTestHelpers, rtsTypes); builder .SetContracts ([ nameof(Contracts.RuntimeTypeSystem) ]) @@ -49,32 +53,30 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) }); } - private void AddSystemObject(MockRTS rtsBuilder, TargetPointer systemObjectMethodTablePtr, TargetPointer systemObjectEEClassPtr) + private (TargetPointer MethodTable, TargetPointer EEClass) AddSystemObjectMethodTable(MockRTS rtsBuilder) { MockMemorySpace.Builder builder = rtsBuilder.Builder; - Dictionary types = rtsBuilder.Types; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numMethods = 8; // System.Object has 8 methods const int numVirtuals = 3; // System.Object has 3 virtual methods - rtsBuilder.AddEEClass(systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - rtsBuilder.AddMethodTable(systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr, + TargetPointer systemObjectEEClassPtr = rtsBuilder.AddEEClass("System.Object", attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + TargetPointer systemObjectMethodTablePtr = rtsBuilder.AddMethodTable("System.Object", mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); + rtsBuilder.SetEEClassAndCanonMTRefs(systemObjectEEClassPtr, systemObjectMethodTablePtr); + return (MethodTable: systemObjectMethodTablePtr, EEClass: systemObjectEEClassPtr); } [Theory] [ClassData(typeof(MockTarget.StdArch))] public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) { - const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; - const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; - TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); - TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); + TargetPointer systemObjectMethodTablePtr = default; RTSContractHelper(arch, (rtsBuilder) => { - AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + systemObjectMethodTablePtr = AddSystemObjectMethodTable(rtsBuilder).MethodTable; }, (target) => { @@ -90,28 +92,23 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) { - const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; - const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; - TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); - TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); - - const ulong SystemStringMethodTableAddress = 0x00000000_7c002010; - const ulong SystemStringEEClassAddress = 0x00000000_7c0020d0; - TargetPointer systemStringMethodTablePtr = new TargetPointer(SystemStringMethodTableAddress); - TargetPointer systemStringEEClassPtr = new TargetPointer(SystemStringEEClassAddress); + TargetPointer systemStringMethodTablePtr = default; + TargetPointer systemStringEEClassPtr = default; RTSContractHelper(arch, (rtsBuilder) => { - AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + TargetPointer systemObjectMethodTablePtr = AddSystemObjectMethodTable(rtsBuilder).MethodTable; + System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed; const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; - rtsBuilder.AddEEClass(systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - rtsBuilder.AddMethodTable(systemStringMethodTablePtr, "System.String", systemStringEEClassPtr, + systemStringEEClassPtr = rtsBuilder.AddEEClass("System.String", attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + systemStringMethodTablePtr = rtsBuilder.AddMethodTable("System.String", mtflags: mtflags, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.StringBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + rtsBuilder.SetEEClassAndCanonMTRefs(systemStringEEClassPtr, systemStringMethodTablePtr); }, (target) => { @@ -128,20 +125,15 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) { - const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; - const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; - TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); - TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); - - const ulong badMethodTableAddress = 0x00000000_4a000100; // place a normal-looking MethodTable here - const ulong badMethodTableEEClassAddress = 0x00000010_afafafafa0; // bad address - TargetPointer badMethodTablePtr = new TargetPointer(badMethodTableAddress); - TargetPointer badMethodTableEEClassPtr = new TargetPointer(badMethodTableEEClassAddress); + TargetPointer badMethodTablePtr = default; + TargetPointer badMethodTableEEClassPtr = new TargetPointer(0x00000010_afafafafa0); // bad address RTSContractHelper(arch, (rtsBuilder) => { - AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); - rtsBuilder.AddMethodTable(badMethodTablePtr, "Bad MethodTable", badMethodTableEEClassPtr, mtflags: default, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); + TargetPointer systemObjectMethodTablePtr = AddSystemObjectMethodTable(rtsBuilder).MethodTable; + badMethodTablePtr = rtsBuilder.AddMethodTable("Bad MethodTable", mtflags: default, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: 0, numVirtuals: 3); + // make the method table point at a bad EEClass + rtsBuilder.SetMethodTableEEClassOrCanonMTRaw(badMethodTablePtr, badMethodTableEEClassPtr); }, (target) => { @@ -155,41 +147,32 @@ public void MethodTableEEClassInvalidThrows(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) { - TargetTestHelpers targetTestHelpers = new(arch); - const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; - const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; - TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); - TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); - - const ulong genericDefinitionMethodTableAddress = 0x00000000_5d004040; - const ulong genericDefinitionEEClassAddress = 0x00000000_5d0040c0; - TargetPointer genericDefinitionMethodTablePtr = new TargetPointer(genericDefinitionMethodTableAddress); - TargetPointer genericDefinitionEEClassPtr = new TargetPointer(genericDefinitionEEClassAddress); - - const ulong genericInstanceMethodTableAddress = 0x00000000_330000a0; - TargetPointer genericInstanceMethodTablePtr = new TargetPointer(genericInstanceMethodTableAddress); + TargetPointer genericDefinitionMethodTablePtr = default; + TargetPointer genericInstanceMethodTablePtr = default; const int numMethods = 17; RTSContractHelper(arch, (rtsBuilder) => { - AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + TargetTestHelpers targetTestHelpers = rtsBuilder.Builder.TargetTestHelpers; + TargetPointer systemObjectMethodTablePtr = AddSystemObjectMethodTable(rtsBuilder).MethodTable; System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class; const int numInterfaces = 0; const int numVirtuals = 3; const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst - rtsBuilder.AddEEClass(genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); - rtsBuilder.AddMethodTable(genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr, + TargetPointer genericDefinitionEEClassPtr = rtsBuilder.AddEEClass("EEClass GenericDefinition", attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); + genericDefinitionMethodTablePtr = rtsBuilder.AddMethodTable("MethodTable GenericDefinition", mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + rtsBuilder.SetEEClassAndCanonMTRefs(genericDefinitionEEClassPtr, genericDefinitionMethodTablePtr); const uint ginst_mtflags = 0x00000010; // TODO: GenericsMask_GenericInst - TargetPointer ginstCanonMT = new TargetPointer(genericDefinitionMethodTablePtr.Value | (ulong)1); - rtsBuilder.AddMethodTable(genericInstanceMethodTablePtr, "MethodTable GenericInstance", eeClassOrCanonMT: ginstCanonMT, + genericInstanceMethodTablePtr = rtsBuilder.AddMethodTable("MethodTable GenericInstance", mtflags: ginst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: genericDefinitionMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals); + rtsBuilder.SetMethodTableCanonMT(genericInstanceMethodTablePtr, genericDefinitionMethodTablePtr); }, (target) => @@ -208,44 +191,33 @@ public void ValidateGenericInstMethodTable(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) { - TargetTestHelpers targetTestHelpers = new(arch); - const ulong SystemObjectMethodTableAddress = 0x00000000_7c000010; - const ulong SystemObjectEEClassAddress = 0x00000000_7c0000d0; - TargetPointer systemObjectMethodTablePtr = new TargetPointer(SystemObjectMethodTableAddress); - TargetPointer systemObjectEEClassPtr = new TargetPointer(SystemObjectEEClassAddress); - - const ulong SystemArrayMethodTableAddress = 0x00000000_7c00a010; - const ulong SystemArrayEEClassAddress = 0x00000000_7c00a0d0; - TargetPointer systemArrayMethodTablePtr = new TargetPointer(SystemArrayMethodTableAddress); - TargetPointer systemArrayEEClassPtr = new TargetPointer(SystemArrayEEClassAddress); - - const ulong arrayInstanceMethodTableAddress = 0x00000000_330000a0; - const ulong arrayInstanceEEClassAddress = 0x00000000_330001d0; - TargetPointer arrayInstanceMethodTablePtr = new TargetPointer(arrayInstanceMethodTableAddress); - TargetPointer arrayInstanceEEClassPtr = new TargetPointer(arrayInstanceEEClassAddress); + TargetPointer arrayInstanceMethodTablePtr = default; const uint arrayInstanceComponentSize = 392; RTSContractHelper(arch, (rtsBuilder) => { - AddSystemObject(rtsBuilder, systemObjectMethodTablePtr, systemObjectEEClassPtr); + TargetTestHelpers targetTestHelpers = rtsBuilder.Builder.TargetTestHelpers; + TargetPointer systemObjectMethodTablePtr = AddSystemObjectMethodTable(rtsBuilder).MethodTable; const ushort systemArrayNumInterfaces = 4; const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class); - rtsBuilder.AddEEClass(systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - rtsBuilder.AddMethodTable(systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr, + TargetPointer systemArrayEEClassPtr = rtsBuilder.AddEEClass("EEClass System.Array", attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + TargetPointer systemArrayMethodTablePtr = rtsBuilder.AddMethodTable("MethodTable System.Array", mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); + rtsBuilder.SetEEClassAndCanonMTRefs(systemArrayEEClassPtr, systemArrayMethodTablePtr); const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); - rtsBuilder.AddEEClass(arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); - rtsBuilder.AddMethodTable(arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr, + TargetPointer arrayInstanceEEClassPtr = rtsBuilder.AddEEClass("EEClass ArrayInstance", attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); + arrayInstanceMethodTablePtr = rtsBuilder.AddMethodTable("MethodTable ArrayInstance", mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize, module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); + rtsBuilder.SetEEClassAndCanonMTRefs(arrayInstanceEEClassPtr, arrayInstanceMethodTablePtr); }, (target) => { diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index f1558a4ae8cd4..8ab8d5c63ee4a 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -146,6 +146,8 @@ internal static readonly (string Name, ulong Value, string? Type)[] Globals = internal readonly MockMemorySpace.Builder Builder; internal readonly Dictionary Types; + internal MockMemorySpace.BumpAllocator TypeSystemAllocator { get; set; } + internal RuntimeTypeSystem(Dictionary types, MockMemorySpace.Builder builder) { Types = types; @@ -170,47 +172,71 @@ private void AddFreeObjectMethodTable() ]); } - internal void AddEEClass(TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots) + // set the eeClass MethodTable pointer to the canonMT and the canonMT's EEClass pointer to the eeClass + internal void SetEEClassAndCanonMTRefs(TargetPointer eeClass, TargetPointer canonMT) + { + // make eeClass point at the canonMT + Target.TypeInfo eeClassTypeInfo = Types[DataType.EEClass]; + Span eeClassBytes = Builder.BorrowAddressRange(eeClass, (int)eeClassTypeInfo.Size.Value); + Builder.TargetTestHelpers.WritePointer(eeClassBytes.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset, Builder.TargetTestHelpers.PointerSize), canonMT); + + // and make the canonMT point at the eeClass + SetMethodTableEEClassOrCanonMTRaw(canonMT, eeClass); + } + + + // for cases when a methodTable needs to point at a canonical method table + internal void SetMethodTableCanonMT(TargetPointer methodTable, TargetPointer canonMT) => SetMethodTableEEClassOrCanonMTRaw(methodTable, canonMT.Value | 1); + + // NOTE: don't use directly unless you want to write a bogus value into the canonMT field + internal void SetMethodTableEEClassOrCanonMTRaw(TargetPointer methodTable, TargetPointer eeClassOrCanonMT) + { + Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; + Span methodTableBytes = Builder.BorrowAddressRange(methodTable, (int)methodTableTypeInfo.Size.Value); + Builder.TargetTestHelpers.WritePointer(methodTableBytes.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset, Builder.TargetTestHelpers.PointerSize), eeClassOrCanonMT); + } + + // call SetEEClassAndCanonMTRefs after the EEClass and the MethodTable have been added + internal TargetPointer AddEEClass(string name, uint attr, ushort numMethods, ushort numNonVirtualSlots) { Target.TypeInfo eeClassTypeInfo = Types[DataType.EEClass]; MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; - MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(eeClassTypeInfo)] }; + + MockMemorySpace.HeapFragment eeClassFragment = TypeSystemAllocator.Allocate(eeClassTypeInfo.Size.Value, $"EEClass '{name}'"); Span dest = eeClassFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); builder.AddHeapFragment(eeClassFragment); + return eeClassFragment.Address; } - internal void AddArrayClass(TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) + internal TargetPointer AddArrayClass(string name, uint attr, ushort numMethods, ushort numNonVirtualSlots, byte rank) { Dictionary types = Types; MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo eeClassTypeInfo = types[DataType.EEClass]; Target.TypeInfo arrayClassTypeInfo = types[DataType.ArrayClass]; - int size = (int)arrayClassTypeInfo.Size.Value; - MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"ArrayClass '{name}'", Address = eeClassPtr, Data = new byte[size] }; + MockMemorySpace.HeapFragment eeClassFragment = TypeSystemAllocator.Allocate (arrayClassTypeInfo.Size.Value, $"ArrayClass '{name}'"); Span dest = eeClassFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods); targetTestHelpers.Write(dest.Slice(eeClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots); targetTestHelpers.Write(dest.Slice(arrayClassTypeInfo.Fields[nameof(Data.ArrayClass.Rank)].Offset), rank); builder.AddHeapFragment(eeClassFragment); + return eeClassFragment.Address; } - internal void AddMethodTable(TargetPointer methodTablePtr, string name, TargetPointer eeClassOrCanonMT, uint mtflags, uint mtflags2, uint baseSize, + internal TargetPointer AddMethodTable(string name, uint mtflags, uint mtflags2, uint baseSize, TargetPointer module, TargetPointer parentMethodTable, ushort numInterfaces, ushort numVirtuals) { Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; - MockMemorySpace.HeapFragment methodTableFragment = new() { Name = $"MethodTable '{name}'", Address = methodTablePtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] }; + MockMemorySpace.HeapFragment methodTableFragment = TypeSystemAllocator.Allocate(methodTableTypeInfo.Size.Value, $"MethodTable '{name}'"); Span dest = methodTableFragment.Data; - targetTestHelpers.WritePointer(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.EEClassOrCanonMT)].Offset), eeClassOrCanonMT); targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags)].Offset), mtflags); targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.MTFlags2)].Offset), mtflags2); targetTestHelpers.Write(dest.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.BaseSize)].Offset), baseSize); @@ -221,6 +247,7 @@ internal void AddMethodTable(TargetPointer methodTablePtr, string name, TargetPo // TODO fill in the rest of the fields builder.AddHeapFragment(methodTableFragment); + return methodTableFragment.Address; } } @@ -402,8 +429,8 @@ internal void AddArrayObject(TargetPointer address, Array array) if (!isSingleDimensionZeroLowerBound) size += array.Rank * sizeof(int) * 2; - ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); - ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); + // ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); + //ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); uint flags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; if (isSingleDimensionZeroLowerBound) @@ -411,11 +438,12 @@ internal void AddArrayObject(TargetPointer address, Array array) string name = string.Join(',', array); - RTSBuilder.AddArrayClass(arrayClassAddress, name, methodTableAddress, + TargetPointer arrayClassAddress = RTSBuilder.AddArrayClass(name, attr: 0, numMethods: 0, numNonVirtualSlots: 0, rank: (byte)array.Rank); - RTSBuilder.AddMethodTable(methodTableAddress, name, arrayClassAddress, + TargetPointer methodTableAddress = RTSBuilder.AddMethodTable(name, mtflags: flags, mtflags2: default, baseSize: targetTestHelpers.ArrayBaseBaseSize, module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0); + RTSBuilder.SetEEClassAndCanonMTRefs(arrayClassAddress, methodTableAddress); MockMemorySpace.HeapFragment fragment = new() { Name = $"Array = '{string.Join(',', array)}'", Address = address, Data = new byte[size] }; Span dest = fragment.Data; diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index bbf9019fb7a2e..8883bd55db6bb 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -19,7 +19,10 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Action types = new(); - MockDescriptors.RuntimeTypeSystem rtsBuilder = new(types, builder); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(types, builder) { + // arbtrary address range + TypeSystemAllocator = builder.CreateAllocator(start: 0x00000000_4a000000, end: 0x00000000_4b000000), + }; MockObject objectBuilder = new(rtsBuilder); MockObject.AddTypes(types, targetTestHelpers); builder = builder From 25adc47a3a582f60502cb64bd45d243edf90753a Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 14:52:00 -0400 Subject: [PATCH 14/20] allocate the free object method table using the bump allocator --- .../cdacreader/tests/MethodTableTests.cs | 10 ++++++++-- .../managed/cdacreader/tests/MockDescriptors.cs | 17 +++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 50e67a19cf35c..abd7c49a6eedb 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -43,11 +43,17 @@ private static void RTSContractHelper(MockTarget.Architecture arch, Action + TargetPointer freeObjectMethodTableAddress = default; + RTSContractHelper(arch, + (builder) => + { + freeObjectMethodTableAddress = builder.FreeObjectMethodTableAddress; + }, + (target) => { Contracts.IRuntimeTypeSystem metadataContract = target.Contracts.RuntimeTypeSystem; Assert.NotNull(metadataContract); - Contracts.TypeHandle handle = metadataContract.GetTypeHandle(MockRTS.TestFreeObjectMethodTableAddress); + Contracts.TypeHandle handle = metadataContract.GetTypeHandle(freeObjectMethodTableAddress); Assert.NotEqual(TargetPointer.Null, handle.Address); Assert.True(metadataContract.IsFreeObjectMethodTable(handle)); }); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 8ab8d5c63ee4a..9d5cfd1174327 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -124,7 +124,6 @@ private static readonly (string, DataType)[] ThreadStoreFields = public static class RuntimeTypeSystem { internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; - internal const ulong TestFreeObjectMethodTableAddress = 0x00000000_7a0000a8; internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary types) { @@ -148,6 +147,8 @@ internal static readonly (string Name, ulong Value, string? Type)[] Globals = internal MockMemorySpace.BumpAllocator TypeSystemAllocator { get; set; } + internal TargetPointer FreeObjectMethodTableAddress { get; private set; } + internal RuntimeTypeSystem(Dictionary types, MockMemorySpace.Builder builder) { Types = types; @@ -161,15 +162,15 @@ internal void AddGlobalPointers() private void AddFreeObjectMethodTable() { - MockMemorySpace.Builder builder = Builder; Target.TypeInfo methodTableTypeInfo = Types[DataType.MethodTable]; - TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; + MockMemorySpace.HeapFragment freeObjectMethodTableFragment = TypeSystemAllocator.Allocate(methodTableTypeInfo.Size.Value, "Free Object Method Table"); + Builder.AddHeapFragment(freeObjectMethodTableFragment); + FreeObjectMethodTableAddress = freeObjectMethodTableFragment.Address; + + TargetTestHelpers targetTestHelpers = Builder.TargetTestHelpers; MockMemorySpace.HeapFragment globalAddr = new() { Name = "Address of Free Object Method Table", Address = TestFreeObjectMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; - targetTestHelpers.WritePointer(globalAddr.Data, TestFreeObjectMethodTableAddress); - builder.AddHeapFragments([ - globalAddr, - new () { Name = "Free Object Method Table", Address = TestFreeObjectMethodTableAddress, Data = new byte[targetTestHelpers.SizeOfTypeInfo(methodTableTypeInfo)] } - ]); + targetTestHelpers.WritePointer(globalAddr.Data, FreeObjectMethodTableAddress); + Builder.AddHeapFragment(globalAddr); } // set the eeClass MethodTable pointer to the canonMT and the canonMT's EEClass pointer to the eeClass From d0d4d0bbc42301af4ae1f07a0abc7d77cc710d6f Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 15:39:37 -0400 Subject: [PATCH 15/20] bump allocation for managed strings and arrays --- .../cdacreader/tests/MockDescriptors.cs | 73 +++++++++++-------- .../managed/cdacreader/tests/ObjectTests.cs | 33 +++++---- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 9d5cfd1174327..f6f0a54f699a7 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -255,7 +255,7 @@ internal TargetPointer AddMethodTable(string name, uint mtflags, uint mtflags2, public class Object { private const ulong TestStringMethodTableGlobalAddress = 0x00000000_100000a0; - private const ulong TestStringMethodTableAddress = 0x00000000_100000a8; + internal const ulong TestArrayBoundsZeroGlobalAddress = 0x00000000_100000b0; private const ulong TestSyncTableEntriesGlobalAddress = 0x00000000_100000c0; @@ -268,9 +268,18 @@ public class Object internal Dictionary Types => RTSBuilder.Types; internal MockMemorySpace.Builder Builder => RTSBuilder.Builder; + internal MockMemorySpace.BumpAllocator ManagedObjectAllocator { get; set; } + + internal MockMemorySpace.BumpAllocator SyncBlockAllocator { get; private set; } + + internal TargetPointer TestStringMethodTableAddress { get; private set; } + internal Object(RuntimeTypeSystem rtsBuilder) { RTSBuilder = rtsBuilder; + + const ulong TestSyncBlocksAddress = 0x00000000_e0000000; + SyncBlockAllocator = Builder.CreateAllocator(start: TestSyncBlocksAddress, end: TestSyncBlocksAddress + 0x1000); } internal static void AddTypes(Dictionary types, TargetTestHelpers helpers) @@ -313,12 +322,11 @@ private void AddStringMethodTablePointer() { MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; + MockMemorySpace.HeapFragment stringMethodTableFragment = RTSBuilder.TypeSystemAllocator.Allocate((ulong)targetTestHelpers.PointerSize /*HACK*/, "String Method Table (fake)"); + TestStringMethodTableAddress = stringMethodTableFragment.Address; MockMemorySpace.HeapFragment fragment = new() { Name = "Address of String Method Table", Address = TestStringMethodTableGlobalAddress, Data = new byte[targetTestHelpers.PointerSize] }; - targetTestHelpers.WritePointer(fragment.Data, TestStringMethodTableAddress); - builder.AddHeapFragments([ - fragment, - new () { Name = "String Method Table", Address = TestStringMethodTableAddress, Data = new byte[targetTestHelpers.PointerSize] } - ]); + targetTestHelpers.WritePointer(fragment.Data, stringMethodTableFragment.Address); + builder.AddHeapFragment(fragment); } private void AddSyncTableEntriesPointer() @@ -330,18 +338,21 @@ private void AddSyncTableEntriesPointer() builder.AddHeapFragment(fragment); } - internal void AddObject(TargetPointer address, TargetPointer methodTable) + internal TargetPointer AddObject(TargetPointer methodTable, uint prefixSize =0) { MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; Target.TypeInfo objectTypeInfo = Types[DataType.Object]; - MockMemorySpace.HeapFragment fragment = new() { Name = $"Object : MT = '{methodTable}'", Address = address, Data = new byte[targetTestHelpers.SizeOfTypeInfo(objectTypeInfo)] }; - Span dest = fragment.Data; + uint totalSize = objectTypeInfo.Size.Value + prefixSize; + MockMemorySpace.HeapFragment fragment = ManagedObjectAllocator.Allocate(totalSize, $"Object : MT = '{methodTable}'"); + + Span dest = fragment.Data.AsSpan((int)prefixSize); targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTable); builder.AddHeapFragment(fragment); + return fragment.Address + prefixSize; // return pointer to the object, not the prefix; } - internal void AddObjectWithSyncBlock(TargetPointer address, TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) + internal TargetPointer AddObjectWithSyncBlock(TargetPointer methodTable, uint syncBlockIndex, TargetPointer rcw, TargetPointer ccw) { MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; @@ -350,29 +361,33 @@ internal void AddObjectWithSyncBlock(TargetPointer address, TargetPointer method if ((syncBlockIndex & SyncBlockIndexMask) != syncBlockIndex) throw new ArgumentOutOfRangeException(nameof(syncBlockIndex), "Invalid sync block index"); - AddObject(address, methodTable); + TargetPointer address = AddObject(methodTable, prefixSize: (uint)TestSyncBlockValueToObjectOffset); // Add the sync table value before the object uint syncTableValue = IsSyncBlockIndexBits | syncBlockIndex; TargetPointer syncTableValueAddr = address - TestSyncBlockValueToObjectOffset; - MockMemorySpace.HeapFragment fragment = new() { Name = $"Sync Table Value : index = {syncBlockIndex}", Address = syncTableValueAddr, Data = new byte[sizeof(uint)] }; - targetTestHelpers.Write(fragment.Data, syncTableValue); - builder.AddHeapFragment(fragment); + Span syncTableValueDest = builder.BorrowAddressRange(syncTableValueAddr, sizeof(uint)); + targetTestHelpers.Write(syncTableValueDest, syncTableValue); // Add the actual sync block and associated data - AddSyncBlock(Types, builder, syncBlockIndex, rcw, ccw); + AddSyncBlock(syncBlockIndex, rcw, ccw); + return address; } - private static void AddSyncBlock(Dictionary types, MockMemorySpace.Builder builder, uint index, TargetPointer rcw, TargetPointer ccw) + private void AddSyncBlock(uint index, TargetPointer rcw, TargetPointer ccw) { + Dictionary types = Types; + MockMemorySpace.Builder builder = Builder; TargetTestHelpers targetTestHelpers = builder.TargetTestHelpers; // Tests write the sync blocks starting at TestSyncBlocksAddress - const ulong TestSyncBlocksAddress = 0x00000000_e0000000; Target.TypeInfo syncBlockTypeInfo = types[DataType.SyncBlock]; Target.TypeInfo interopSyncBlockTypeInfo = types[DataType.InteropSyncBlockInfo]; - int syncBlockSize = targetTestHelpers.SizeOfTypeInfo(syncBlockTypeInfo); - int interopSyncBlockInfoSize = targetTestHelpers.SizeOfTypeInfo(interopSyncBlockTypeInfo); - ulong syncBlockAddr = TestSyncBlocksAddress + index * (ulong)(syncBlockSize + interopSyncBlockInfoSize); + uint syncBlockSize = syncBlockTypeInfo.Size.Value;; + uint interopSyncBlockInfoSize = syncBlockSize + interopSyncBlockTypeInfo.Size.Value; + + + MockMemorySpace.HeapFragment syncBlock = SyncBlockAllocator.Allocate(interopSyncBlockInfoSize, $"Sync Block {index}"); + TargetPointer syncBlockAddr = syncBlock.Address; // Add the sync table entry - pointing at the sync block Target.TypeInfo syncTableEntryInfo = types[DataType.SyncTableEntry]; @@ -383,21 +398,19 @@ private static void AddSyncBlock(Dictionary types, Mo targetTestHelpers.WritePointer(syncTableEntryData.Slice(syncTableEntryInfo.Fields[nameof(Data.SyncTableEntry.SyncBlock)].Offset), syncBlockAddr); // Add the sync block - pointing at the interop sync block info - ulong interopInfoAddr = syncBlockAddr + (ulong)syncBlockSize; - MockMemorySpace.HeapFragment syncBlock = new() { Name = $"Sync Block", Address = syncBlockAddr, Data = new byte[syncBlockSize] }; + TargetPointer interopInfoAddr = syncBlockAddr + syncBlockSize; Span syncBlockData = syncBlock.Data; targetTestHelpers.WritePointer(syncBlockData.Slice(syncBlockTypeInfo.Fields[nameof(Data.SyncBlock.InteropInfo)].Offset), interopInfoAddr); // Add the interop sync block info - MockMemorySpace.HeapFragment interopInfo = new() { Name = $"Interop Sync Block Info", Address = interopInfoAddr, Data = new byte[interopSyncBlockInfoSize] }; - Span interopInfoData = interopInfo.Data; + Span interopInfoData = syncBlock.Data.AsSpan((int)syncBlockSize); targetTestHelpers.WritePointer(interopInfoData.Slice(interopSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.RCW)].Offset), rcw); targetTestHelpers.WritePointer(interopInfoData.Slice(interopSyncBlockTypeInfo.Fields[nameof(Data.InteropSyncBlockInfo.CCW)].Offset), ccw); - builder.AddHeapFragments([syncTableEntry, syncBlock, interopInfo]); + builder.AddHeapFragments([syncTableEntry, syncBlock]); } - internal void AddStringObject(TargetPointer address, string value) + internal TargetPointer AddStringObject(string value) { MockMemorySpace.Builder builder = Builder; Dictionary types = Types; @@ -405,15 +418,16 @@ internal void AddStringObject(TargetPointer address, string value) Target.TypeInfo objectTypeInfo = types[DataType.Object]; Target.TypeInfo stringTypeInfo = types[DataType.String]; int size = (int)stringTypeInfo.Size.Value + value.Length * sizeof(char); - MockMemorySpace.HeapFragment fragment = new() { Name = $"String = '{value}'", Address = address, Data = new byte[size] }; + MockMemorySpace.HeapFragment fragment = ManagedObjectAllocator.Allocate((uint)size, $"String = '{value}'"); Span dest = fragment.Data; targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), TestStringMethodTableAddress); targetTestHelpers.Write(dest.Slice(stringTypeInfo.Fields["m_StringLength"].Offset), (uint)value.Length); MemoryMarshal.Cast(value).CopyTo(dest.Slice(stringTypeInfo.Fields["m_FirstChar"].Offset)); builder.AddHeapFragment(fragment); + return fragment.Address; } - internal void AddArrayObject(TargetPointer address, Array array) + internal TargetPointer AddArrayObject(Array array) { MockMemorySpace.Builder builder = Builder; Dictionary types = Types; @@ -446,11 +460,12 @@ internal void AddArrayObject(TargetPointer address, Array array) module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: 0); RTSBuilder.SetEEClassAndCanonMTRefs(arrayClassAddress, methodTableAddress); - MockMemorySpace.HeapFragment fragment = new() { Name = $"Array = '{string.Join(',', array)}'", Address = address, Data = new byte[size] }; + MockMemorySpace.HeapFragment fragment = ManagedObjectAllocator.Allocate((uint)size, $"Array = '{string.Join(',', array)}'"); Span dest = fragment.Data; targetTestHelpers.WritePointer(dest.Slice(objectTypeInfo.Fields["m_pMethTab"].Offset), methodTableAddress); targetTestHelpers.Write(dest.Slice(arrayTypeInfo.Fields["m_NumComponents"].Offset), (uint)array.Length); builder.AddHeapFragment(fragment); + return fragment.Address; } } diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index 8883bd55db6bb..f76a1978f95a5 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -23,7 +23,10 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Action { - objectBuilder.AddObject(TestObjectAddress, TestMethodTableAddress); + TestObjectAddress = objectBuilder.AddObject(TestMethodTableAddress); }, (target) => { @@ -63,12 +66,12 @@ public void UnmaskMethodTableAddress(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void StringValue(MockTarget.Architecture arch) { - const ulong TestStringAddress = 0x00000000_10000010; + TargetPointer TestStringAddress = default; string expected = "test_string_value"; ObjectContractHelper(arch, (objectBuilder) => { - objectBuilder.AddStringObject(TestStringAddress, expected); + TestStringAddress = objectBuilder.AddStringObject(expected); }, (target) => { @@ -83,9 +86,9 @@ public void StringValue(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void ArrayData(MockTarget.Architecture arch) { - const ulong SingleDimensionArrayAddress = 0x00000000_20000010; - const ulong MultiDimensionArrayAddress = 0x00000000_30000010; - const ulong NonZeroLowerBoundArrayAddress = 0x00000000_40000010; + TargetPointer SingleDimensionArrayAddress = default; + TargetPointer MultiDimensionArrayAddress = default; + TargetPointer NonZeroLowerBoundArrayAddress = default; Array singleDimension = new int[10]; Array multiDimension = new int[1, 2, 3, 4]; @@ -94,9 +97,9 @@ public void ArrayData(MockTarget.Architecture arch) ObjectContractHelper(arch, (objectBuilder) => { - objectBuilder.AddArrayObject(SingleDimensionArrayAddress, singleDimension); - objectBuilder.AddArrayObject(MultiDimensionArrayAddress, multiDimension); - objectBuilder.AddArrayObject(NonZeroLowerBoundArrayAddress, nonZeroLowerBound); + SingleDimensionArrayAddress = objectBuilder.AddArrayObject(singleDimension); + MultiDimensionArrayAddress = objectBuilder.AddArrayObject(multiDimension); + NonZeroLowerBoundArrayAddress = objectBuilder.AddArrayObject(nonZeroLowerBound); }, (target) => { @@ -131,8 +134,8 @@ public void ArrayData(MockTarget.Architecture arch) [ClassData(typeof(MockTarget.StdArch))] public void ComData(MockTarget.Architecture arch) { - const ulong TestComObjectAddress = 0x00000000_10000010; - const ulong TestNonComObjectAddress = 0x00000000_10000020; + TargetPointer TestComObjectAddress = default; + TargetPointer TestNonComObjectAddress = default; TargetPointer expectedRCW = 0xaaaa; TargetPointer expectedCCW = 0xbbbb; @@ -141,8 +144,8 @@ public void ComData(MockTarget.Architecture arch) (objectBuilder) => { uint syncBlockIndex = 0; - objectBuilder.AddObjectWithSyncBlock(TestComObjectAddress, 0, syncBlockIndex++, expectedRCW, expectedCCW); - objectBuilder.AddObjectWithSyncBlock(TestNonComObjectAddress, 0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); + TestComObjectAddress = objectBuilder.AddObjectWithSyncBlock(0, syncBlockIndex++, expectedRCW, expectedCCW); + TestNonComObjectAddress = objectBuilder.AddObjectWithSyncBlock(0, syncBlockIndex++, TargetPointer.Null, TargetPointer.Null); }, (target) => { From d799442c5af134251896aa420515788cdd9621f6 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Fri, 25 Oct 2024 15:46:51 -0400 Subject: [PATCH 16/20] cleanup --- src/native/managed/cdacreader/tests/MockDescriptors.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index f6f0a54f699a7..d302d93cd0a79 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -259,6 +259,7 @@ public class Object internal const ulong TestArrayBoundsZeroGlobalAddress = 0x00000000_100000b0; private const ulong TestSyncTableEntriesGlobalAddress = 0x00000000_100000c0; + // The sync table entries address range is manually managed in AddObjectWithSyncBlock private const ulong TestSyncTableEntriesAddress = 0x00000000_f0000000; internal const ulong TestObjectToMethodTableUnmask = 0x7; @@ -315,7 +316,6 @@ internal void AddGlobalPointers() RTSBuilder.AddGlobalPointers(); AddStringMethodTablePointer(); AddSyncTableEntriesPointer(); - } private void AddStringMethodTablePointer() From a884151650d27116c630708ab00a92b26b771d21 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 28 Oct 2024 15:07:47 -0400 Subject: [PATCH 17/20] move MethodTable flags and validation to a separate classes --- .../Contracts/RuntimeTypeSystemFactory.cs | 2 +- .../RuntimeTypeSystem_1.MethodTableFlags.cs | 95 --------- .../RuntimeTypeSystem_1.NonValidated.cs | 178 ---------------- .../Contracts/RuntimeTypeSystem_1.cs | 53 ++--- .../ExtensionMethods.cs} | 8 +- .../MethodTableFlags_1.cs | 117 ++++++++++ .../TypeValidation.cs | 199 ++++++++++++++++++ .../cdacreader/tests/MethodTableTests.cs | 6 +- .../cdacreader/tests/MockDescriptors.cs | 7 +- 9 files changed, 350 insertions(+), 315 deletions(-) delete mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs rename src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/{Contracts/RuntimeTypeSystem_1_Helpers.cs => RuntimeTypeSystemHelpers/ExtensionMethods.cs} (76%) create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.cs create mode 100644 src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/TypeValidation.cs diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystemFactory.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystemFactory.cs index 8590f3e11a7cf..1f1ab288793a5 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystemFactory.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystemFactory.cs @@ -14,7 +14,7 @@ IRuntimeTypeSystem IContractFactory.CreateContract(Target ta ulong methodDescAlignment = target.ReadGlobal(Constants.Globals.MethodDescAlignment); return version switch { - 1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable, methodDescAlignment), + 1 => new RuntimeTypeSystem_1(target, new RuntimeTypeSystemHelpers.TypeValidation(target), freeObjectMethodTable, methodDescAlignment), _ => default(RuntimeTypeSystem), }; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs deleted file mode 100644 index 6e02d241b994c..0000000000000 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.MethodTableFlags.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Diagnostics.DataContractReader.Contracts; - -internal partial struct RuntimeTypeSystem_1 -{ - // The lower 16-bits of the MTFlags field are used for these flags, - // if WFLAGS_HIGH.HasComponentSize is unset - [Flags] - internal enum WFLAGS_LOW : uint - { - GenericsMask = 0x00000030, - GenericsMask_NonGeneric = 0x00000000, // no instantiation - GenericsMask_TypicalInstantiation = 0x00000030, // the type instantiated at its formal parameters, e.g. List - - StringArrayValues = - GenericsMask_NonGeneric | - 0, - } - - // Upper bits of MTFlags - [Flags] - internal enum WFLAGS_HIGH : uint - { - Category_Mask = 0x000F0000, - Category_Array = 0x00080000, - Category_IfArrayThenSzArray = 0x00020000, - Category_Array_Mask = 0x000C0000, - Category_ElementType_Mask = 0x000E0000, - Category_ValueType = 0x00040000, - Category_Nullable = 0x00050000, - Category_PrimitiveValueType = 0x00060000, - Category_TruePrimitive = 0x00070000, - Category_Interface = 0x000C0000, - ContainsGCPointers = 0x01000000, - HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size, - // otherwise the lower bits are used for WFLAGS_LOW - } - - [Flags] - internal enum WFLAGS2_ENUM : uint - { - DynamicStatics = 0x0002, - } - - internal struct MethodTableFlags - { - public uint MTFlags { get; init; } - public uint MTFlags2 { get; init; } - public uint BaseSize { get; init; } - - private const int MTFlags2TypeDefRidShift = 8; - private WFLAGS_HIGH FlagsHigh => (WFLAGS_HIGH)MTFlags; - private WFLAGS_LOW FlagsLow => (WFLAGS_LOW)MTFlags; - public int GetTypeDefRid() => (int)(MTFlags2 >> MTFlags2TypeDefRidShift); - - public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) => throw new NotImplementedException("TODO"); - public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) => FlagsHigh & mask; - - public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) => (WFLAGS2_ENUM)MTFlags2 & mask; - - private ushort ComponentSizeBits => (ushort)(MTFlags & 0x0000ffff); // note: caller should check HasComponentSize - - private bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag) - { - if (IsStringOrArray) - { - return (WFLAGS_LOW.StringArrayValues & mask) == flag; - } - else - { - return (FlagsLow & mask) == flag; - } - } - - private bool TestFlagWithMask(WFLAGS2_ENUM mask, WFLAGS2_ENUM flag) - { - return ((WFLAGS2_ENUM)MTFlags2 & mask) == flag; - } - - public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0; - public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface; - public bool IsString => HasComponentSize && !IsArray && ComponentSizeBits == 2; - public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array; - public bool IsStringOrArray => HasComponentSize; - public ushort ComponentSize => HasComponentSize ? ComponentSizeBits : (ushort)0; - public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric); - public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0; - public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0; - public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation); - } -} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.NonValidated.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.NonValidated.cs index b319f728eaf91..958767b7a1730 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.NonValidated.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.NonValidated.cs @@ -15,77 +15,6 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem internal static class NonValidated { - // This doesn't need as many properties as MethodTable because we don't want to be operating on - // a NonValidatedMethodTable for too long - internal struct MethodTable - { - private readonly Target _target; - private readonly Target.TypeInfo _type; - internal TargetPointer Address { get; init; } - - private MethodTableFlags? _methodTableFlags; - - internal MethodTable(Target target, TargetPointer methodTablePointer) - { - _target = target; - _type = target.GetTypeInfo(DataType.MethodTable); - Address = methodTablePointer; - _methodTableFlags = null; - } - - private MethodTableFlags GetOrCreateFlags() - { - if (_methodTableFlags == null) - { - // note: may throw if the method table Address is corrupted - MethodTableFlags flags = new MethodTableFlags - { - MTFlags = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags)].Offset), - MTFlags2 = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.MTFlags2)].Offset), - BaseSize = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags.BaseSize)].Offset), - }; - _methodTableFlags = flags; - } - return _methodTableFlags.Value; - } - - internal MethodTableFlags Flags => GetOrCreateFlags(); - - internal TargetPointer EEClassOrCanonMT => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(EEClassOrCanonMT)].Offset); - internal TargetPointer EEClass => GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass"); - internal TargetPointer CanonMT - { - get - { - if (GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.CanonMT) - { - return new TargetPointer((ulong)EEClassOrCanonMT & ~(ulong)EEClassOrCanonMTBits.Mask); - } - else - { - throw new InvalidOperationException("not a canonical method table"); - } - } - } - } - - internal struct EEClass - { - public readonly Target _target; - private readonly Target.TypeInfo _type; - - internal TargetPointer Address { get; init; } - - internal EEClass(Target target, TargetPointer eeClassPointer) - { - _target = target; - Address = eeClassPointer; - _type = target.GetTypeInfo(DataType.EEClass); - } - - internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset); - } - internal struct MethodDesc { private readonly Target _target; @@ -106,113 +35,6 @@ internal MethodDesc(Target target, Data.MethodDesc desc, Data.MethodDescChunk ch internal bool HasNonVtableSlot => HasFlag(MethodDescFlags.HasNonVtableSlot); } - internal static MethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer) - { - return new MethodTable(target, methodTablePointer); - } - - internal static EEClass GetEEClassData(Target target, TargetPointer eeClassPointer) - { - return new EEClass(target, eeClassPointer); - } - - } - - /// - /// Validates that the given address is a valid MethodTable. - /// - /// - /// If the target process has memory corruption, we may see pointers that are not valid method tables. - /// We validate by looking at the MethodTable -> EEClass -> MethodTable relationship (which may throw if we access invalid memory). - /// And then we do some ad-hoc checks on the method table flags. - private bool ValidateMethodTablePointer(NonValidated.MethodTable umt) - { - try - { - if (!ValidateThrowing(umt)) - { - return false; - } - if (!ValidateMethodTableAdHoc(umt)) - { - return false; - } - } - catch (System.Exception) - { - // TODO(cdac): maybe don't swallow all exceptions? We could consider a richer contract that - // helps to track down what sort of memory corruption caused the validation to fail. - // TODO(cdac): we could also consider a more fine-grained exception type so we don't mask - // programmer mistakes in cdacreader. - return false; - } - return true; - } - - // This portion of validation may throw if we are trying to read an invalid address in the target process - private bool ValidateThrowing(NonValidated.MethodTable methodTable) - { - // For non-generic classes, we can rely on comparing - // object->methodtable->class->methodtable - // to - // object->methodtable - // - // However, for generic instantiation this does not work. There we must - // compare - // - // object->methodtable->class->methodtable->class - // to - // object->methodtable->class - TargetPointer eeClassPtr = GetClassThrowing(methodTable); - if (eeClassPtr != TargetPointer.Null) - { - NonValidated.EEClass eeClass = NonValidated.GetEEClassData(_target, eeClassPtr); - TargetPointer methodTablePtrFromClass = eeClass.MethodTable; - if (methodTable.Address == methodTablePtrFromClass) - { - return true; - } - if (methodTable.Flags.HasInstantiation || methodTable.Flags.IsArray) - { - NonValidated.MethodTable methodTableFromClass = NonValidated.GetMethodTableData(_target, methodTablePtrFromClass); - TargetPointer classFromMethodTable = GetClassThrowing(methodTableFromClass); - return classFromMethodTable == eeClassPtr; - } - } - return false; - } - - private bool ValidateMethodTableAdHoc(NonValidated.MethodTable methodTable) - { - // ad-hoc checks; add more here as needed - if (!methodTable.Flags.IsInterface && !methodTable.Flags.IsString) - { - if (methodTable.Flags.BaseSize == 0 || !_target.IsAlignedToPointerSize(methodTable.Flags.BaseSize)) - { - return false; - } - } - return true; - } - - internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr) - { - return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask); - } - private TargetPointer GetClassThrowing(NonValidated.MethodTable methodTable) - { - TargetPointer eeClassOrCanonMT = methodTable.EEClassOrCanonMT; - - if (GetEEClassOrCanonMTBits(eeClassOrCanonMT) == EEClassOrCanonMTBits.EEClass) - { - return methodTable.EEClass; - } - else - { - TargetPointer canonicalMethodTablePtr = methodTable.CanonMT; - NonValidated.MethodTable umt = NonValidated.GetMethodTableData(_target, canonicalMethodTablePtr); - return umt.EEClass; - } } private TargetPointer GetMethodDescChunkPointerThrowing(TargetPointer methodDescPointer, Data.MethodDesc umd) 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 654651ce20d5e..32b08e80ac9a4 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 @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Reflection.Metadata.Ecma335; -using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS; +using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -15,6 +15,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem private readonly Target _target; private readonly TargetPointer _freeObjectMethodTablePointer; private readonly ulong _methodDescAlignment; + private readonly TypeValidation _typeValidation; // TODO(cdac): we mutate this dictionary - copies of the RuntimeTypeSystem_1 struct share this instance. // If we need to invalidate our view of memory, we should clear this dictionary. @@ -24,7 +25,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem internal struct MethodTable { - internal MethodTableFlags Flags { get; } + internal MethodTableFlags_1 Flags { get; } internal ushort NumInterfaces { get; } internal ushort NumVirtuals { get; } internal TargetPointer ParentMethodTable { get; } @@ -33,7 +34,7 @@ internal struct MethodTable internal TargetPointer PerInstInfo { get; } internal MethodTable(Data.MethodTable data) { - Flags = new MethodTableFlags + Flags = new MethodTableFlags_1 { MTFlags = data.MTFlags, MTFlags2 = data.MTFlags2, @@ -48,17 +49,8 @@ internal MethodTable(Data.MethodTable data) } // this MethodTable is a canonical MethodTable if its EEClassOrCanonMT is an EEClass - internal bool IsCanonMT => GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.EEClass; - } + internal bool IsCanonMT => MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass; - // Low order bit of EEClassOrCanonMT. - // See MethodTable::LowBits UNION_EECLASS / UNION_METHODABLE - [Flags] - internal enum EEClassOrCanonMTBits - { - EEClass = 0, - CanonMT = 1, - Mask = 1, } // Low order bits of TypeHandle address. @@ -222,11 +214,12 @@ private StoredSigMethodDesc(Target target, TargetPointer methodDescPointer) } } - internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer, ulong methodDescAlignment) + internal RuntimeTypeSystem_1(Target target, TypeValidation typeValidation, TargetPointer freeObjectMethodTablePointer, ulong methodDescAlignment) { _target = target; _freeObjectMethodTablePointer = freeObjectMethodTablePointer; _methodDescAlignment = methodDescAlignment; + _typeValidation = typeValidation; } internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer; @@ -276,9 +269,7 @@ public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer) } // Otherwse, get ready to validate - NonValidated.MethodTable nonvalidatedMethodTable = NonValidated.GetMethodTableData(_target, methodTablePointer); - - if (!ValidateMethodTablePointer(nonvalidatedMethodTable)) + if (!_typeValidation.TryValidateMethodTablePointer(methodTablePointer)) { throw new InvalidOperationException("Invalid method table pointer"); } @@ -296,12 +287,12 @@ public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer) private TargetPointer GetClassPointer(TypeHandle typeHandle) { MethodTable methodTable = _methodTables[typeHandle.Address]; - switch (GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT)) + switch (MethodTableFlags_1.GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT)) { - case EEClassOrCanonMTBits.EEClass: + case MethodTableFlags_1.EEClassOrCanonMTBits.EEClass: return methodTable.EEClassOrCanonMT; - case EEClassOrCanonMTBits.CanonMT: - TargetPointer canonMTPtr = new TargetPointer((ulong)methodTable.EEClassOrCanonMT & ~(ulong)RuntimeTypeSystem_1.EEClassOrCanonMTBits.Mask); + case MethodTableFlags_1.EEClassOrCanonMTBits.CanonMT: + TargetPointer canonMTPtr =MethodTableFlags_1.UntagEEClassOrCanonMT(methodTable.EEClassOrCanonMT); TypeHandle canonMTHandle = GetTypeHandle(canonMTPtr); MethodTable canonMT = _methodTables[canonMTHandle.Address]; return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass @@ -440,17 +431,17 @@ public CorElementType GetSignatureCorElementType(TypeHandle typeHandle) { MethodTable methodTable = _methodTables[typeHandle.Address]; - switch (methodTable.Flags.GetFlag(WFLAGS_HIGH.Category_Mask)) + switch (methodTable.Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.Category_Mask)) { - case WFLAGS_HIGH.Category_Array: + case MethodTableFlags_1.WFLAGS_HIGH.Category_Array: return CorElementType.Array; - case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray: + case MethodTableFlags_1.WFLAGS_HIGH.Category_Array | MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray: return CorElementType.SzArray; - case WFLAGS_HIGH.Category_ValueType: - case WFLAGS_HIGH.Category_Nullable: - case WFLAGS_HIGH.Category_PrimitiveValueType: + case MethodTableFlags_1.WFLAGS_HIGH.Category_ValueType: + case MethodTableFlags_1.WFLAGS_HIGH.Category_Nullable: + case MethodTableFlags_1.WFLAGS_HIGH.Category_PrimitiveValueType: return CorElementType.ValueType; - case WFLAGS_HIGH.Category_TruePrimitive: + case MethodTableFlags_1.WFLAGS_HIGH.Category_TruePrimitive: return (CorElementType)GetClassData(typeHandle).InternalCorElementType; default: return CorElementType.Class; @@ -472,14 +463,14 @@ public bool IsArray(TypeHandle typeHandle, out uint rank) { MethodTable methodTable = _methodTables[typeHandle.Address]; - switch (methodTable.Flags.GetFlag(WFLAGS_HIGH.Category_Mask)) + switch (methodTable.Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.Category_Mask)) { - case WFLAGS_HIGH.Category_Array: + case MethodTableFlags_1.WFLAGS_HIGH.Category_Array: TargetPointer clsPtr = GetClassPointer(typeHandle); rank = _target.ProcessedData.GetOrAdd(clsPtr).Rank; return true; - case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray: + case MethodTableFlags_1.WFLAGS_HIGH.Category_Array | MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray: rank = 1; return true; } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1_Helpers.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/ExtensionMethods.cs similarity index 76% rename from src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1_Helpers.cs rename to src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/ExtensionMethods.cs index af1e23df461b9..2dd9a6dde7e94 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1_Helpers.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/ExtensionMethods.cs @@ -1,9 +1,11 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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.Contracts.RuntimeTypeSystem_1_NS; +using Microsoft.Diagnostics.DataContractReader.Contracts; -internal static class RuntimeTypeSystem_1_Helpers +namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; + +internal static class ExtensionMethods { public static bool IsTypeDesc(this TypeHandle type) { diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.cs new file mode 100644 index 0000000000000..0998d5c22c5d7 --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodTableFlags_1.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; + +namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; + +internal struct MethodTableFlags_1 +{ + // Low order bit of EEClassOrCanonMT. + // See MethodTable::LowBits UNION_EECLASS / UNION_METHODABLE + [Flags] + internal enum EEClassOrCanonMTBits + { + EEClass = 0, + CanonMT = 1, + Mask = 1, + } + + // The lower 16-bits of the MTFlags field are used for these flags, + // if WFLAGS_HIGH.HasComponentSize is unset + [Flags] + internal enum WFLAGS_LOW : uint + { + GenericsMask = 0x00000030, + GenericsMask_NonGeneric = 0x00000000, // no instantiation + GenericsMask_TypicalInstantiation = 0x00000030, // the type instantiated at its formal parameters, e.g. List + + StringArrayValues = + GenericsMask_NonGeneric | + 0, + } + + // Upper bits of MTFlags + [Flags] + internal enum WFLAGS_HIGH : uint + { + Category_Mask = 0x000F0000, + Category_Array = 0x00080000, + Category_IfArrayThenSzArray = 0x00020000, + Category_Array_Mask = 0x000C0000, + Category_ElementType_Mask = 0x000E0000, + Category_ValueType = 0x00040000, + Category_Nullable = 0x00050000, + Category_PrimitiveValueType = 0x00060000, + Category_TruePrimitive = 0x00070000, + Category_Interface = 0x000C0000, + ContainsGCPointers = 0x01000000, + HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size, + // otherwise the lower bits are used for WFLAGS_LOW + } + + [Flags] + internal enum WFLAGS2_ENUM : uint + { + DynamicStatics = 0x0002, + } + + public uint MTFlags { get; init; } + public uint MTFlags2 { get; init; } + public uint BaseSize { get; init; } + + private const int MTFlags2TypeDefRidShift = 8; + private WFLAGS_HIGH FlagsHigh => (WFLAGS_HIGH)MTFlags; + private WFLAGS_LOW FlagsLow => (WFLAGS_LOW)MTFlags; + public int GetTypeDefRid() => (int)(MTFlags2 >> MTFlags2TypeDefRidShift); + + public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) => throw new NotImplementedException("TODO"); + public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) => FlagsHigh & mask; + + public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) => (WFLAGS2_ENUM)MTFlags2 & mask; + + private ushort ComponentSizeBits => (ushort)(MTFlags & 0x0000ffff); // note: caller should check HasComponentSize + + private bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag) + { + if (IsStringOrArray) + { + return (WFLAGS_LOW.StringArrayValues & mask) == flag; + } + else + { + return (FlagsLow & mask) == flag; + } + } + + private bool TestFlagWithMask(WFLAGS2_ENUM mask, WFLAGS2_ENUM flag) + { + return ((WFLAGS2_ENUM)MTFlags2 & mask) == flag; + } + + public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0; + public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface; + public bool IsString => HasComponentSize && !IsArray && ComponentSizeBits == 2; + public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array; + public bool IsStringOrArray => HasComponentSize; + public ushort ComponentSize => HasComponentSize ? ComponentSizeBits : (ushort)0; + public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric); + public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0; + public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0; + public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation); + + internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr) + { + return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask); + } + + internal static TargetPointer UntagEEClassOrCanonMT(TargetPointer eeClassOrCanonMTPtr) + { + return eeClassOrCanonMTPtr & ~(ulong)EEClassOrCanonMTBits.Mask; + } + + internal static TargetPointer TagEEClassOrCanonMT(TargetPointer eeClassOrCanonMTPtr, EEClassOrCanonMTBits tag) + { + return (eeClassOrCanonMTPtr & ~(ulong)EEClassOrCanonMTBits.Mask) | (ulong)tag; + } + +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/TypeValidation.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/TypeValidation.cs new file mode 100644 index 0000000000000..25135dce7385b --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/TypeValidation.cs @@ -0,0 +1,199 @@ +// 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 Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Data; + +namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; + +internal class TypeValidation +{ + private readonly Target _target; + + internal TypeValidation(Target target) + { + _target = target; + } + + // This doesn't need as many properties as MethodTable because we don't want to be operating on + // a NonValidatedMethodTable for too long + internal struct NonValidatedMethodTable + { + private readonly Target _target; + private readonly Target.TypeInfo _type; + internal TargetPointer Address { get; init; } + + private MethodTableFlags_1? _methodTableFlags; + + internal NonValidatedMethodTable(Target target, TargetPointer methodTablePointer) + { + _target = target; + _type = target.GetTypeInfo(DataType.MethodTable); + Address = methodTablePointer; + _methodTableFlags = null; + } + + private MethodTableFlags_1 GetOrCreateFlags() + { + if (_methodTableFlags == null) + { + // note: may throw if the method table Address is corrupted + MethodTableFlags_1 flags = new MethodTableFlags_1 + { + MTFlags = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.MTFlags)].Offset), + MTFlags2 = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.MTFlags2)].Offset), + BaseSize = _target.Read(Address + (ulong)_type.Fields[nameof(MethodTableFlags_1.BaseSize)].Offset), + }; + _methodTableFlags = flags; + } + return _methodTableFlags.Value; + } + + internal MethodTableFlags_1 Flags => GetOrCreateFlags(); + + internal TargetPointer EEClassOrCanonMT => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(EEClassOrCanonMT)].Offset); + internal TargetPointer EEClass => MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass ? EEClassOrCanonMT : throw new InvalidOperationException("not an EEClass"); + internal TargetPointer CanonMT + { + get + { + if (MethodTableFlags_1.GetEEClassOrCanonMTBits(EEClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.CanonMT) + { + return MethodTableFlags_1.UntagEEClassOrCanonMT(EEClassOrCanonMT); + } + else + { + throw new InvalidOperationException("not a canonical method table"); + } + } + } + } + + internal struct NonValidatedEEClass + { + public readonly Target _target; + private readonly Target.TypeInfo _type; + + internal TargetPointer Address { get; init; } + + internal NonValidatedEEClass(Target target, TargetPointer eeClassPointer) + { + _target = target; + Address = eeClassPointer; + _type = target.GetTypeInfo(DataType.EEClass); + } + + internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset); + } + + internal static NonValidatedMethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer) + { + return new NonValidatedMethodTable(target, methodTablePointer); + } + + internal static NonValidatedEEClass GetEEClassData(Target target, TargetPointer eeClassPointer) + { + return new NonValidatedEEClass(target, eeClassPointer); + } + + + /// + /// Validates that the given address is a valid MethodTable. + /// + /// + /// If the target process has memory corruption, we may see pointers that are not valid method tables. + /// We validate by looking at the MethodTable -> EEClass -> MethodTable relationship (which may throw if we access invalid memory). + /// And then we do some ad-hoc checks on the method table flags. + private bool ValidateMethodTablePointer(NonValidatedMethodTable umt) + { + try + { + if (!ValidateThrowing(umt)) + { + return false; + } + if (!ValidateMethodTableAdHoc(umt)) + { + return false; + } + } + catch (System.Exception) + { + // TODO(cdac): maybe don't swallow all exceptions? We could consider a richer contract that + // helps to track down what sort of memory corruption caused the validation to fail. + // TODO(cdac): we could also consider a more fine-grained exception type so we don't mask + // programmer mistakes in cdacreader. + return false; + } + return true; + } + + // This portion of validation may throw if we are trying to read an invalid address in the target process + private bool ValidateThrowing(NonValidatedMethodTable methodTable) + { + // For non-generic classes, we can rely on comparing + // object->methodtable->class->methodtable + // to + // object->methodtable + // + // However, for generic instantiation this does not work. There we must + // compare + // + // object->methodtable->class->methodtable->class + // to + // object->methodtable->class + TargetPointer eeClassPtr = GetClassThrowing(methodTable); + if (eeClassPtr != TargetPointer.Null) + { + NonValidatedEEClass eeClass = GetEEClassData(_target, eeClassPtr); + TargetPointer methodTablePtrFromClass = eeClass.MethodTable; + if (methodTable.Address == methodTablePtrFromClass) + { + return true; + } + if (methodTable.Flags.HasInstantiation || methodTable.Flags.IsArray) + { + NonValidatedMethodTable methodTableFromClass = GetMethodTableData(_target, methodTablePtrFromClass); + TargetPointer classFromMethodTable = GetClassThrowing(methodTableFromClass); + return classFromMethodTable == eeClassPtr; + } + } + return false; + } + + private bool ValidateMethodTableAdHoc(NonValidatedMethodTable methodTable) + { + // ad-hoc checks; add more here as needed + if (!methodTable.Flags.IsInterface && !methodTable.Flags.IsString) + { + if (methodTable.Flags.BaseSize == 0 || !_target.IsAlignedToPointerSize(methodTable.Flags.BaseSize)) + { + return false; + } + } + return true; + } + + private TargetPointer GetClassThrowing(NonValidatedMethodTable methodTable) + { + TargetPointer eeClassOrCanonMT = methodTable.EEClassOrCanonMT; + + if (MethodTableFlags_1.GetEEClassOrCanonMTBits(eeClassOrCanonMT) == MethodTableFlags_1.EEClassOrCanonMTBits.EEClass) + { + return methodTable.EEClass; + } + else + { + TargetPointer canonicalMethodTablePtr = methodTable.CanonMT; + NonValidatedMethodTable umt = GetMethodTableData(_target, canonicalMethodTablePtr); + return umt.EEClass; + } + } + + internal bool TryValidateMethodTablePointer(TargetPointer methodTablePointer) + { + NonValidatedMethodTable nonvalidatedMethodTable = GetMethodTableData(_target, methodTablePointer); + + return ValidateMethodTablePointer(nonvalidatedMethodTable); + } +} diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index abd7c49a6eedb..3004f5872263f 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; -using Microsoft.Diagnostics.DataContractReader.Data; +using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; using Xunit; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -109,7 +109,7 @@ public void ValidateSystemStringMethodTable(MockTarget.Architecture arch) const int numMethods = 37; // Arbitrary. Not trying to exactly match the real System.String const int numInterfaces = 8; // Arbitrary const int numVirtuals = 3; // at least as many as System.Object - uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; + uint mtflags = (uint)MethodTableFlags_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2; systemStringEEClassPtr = rtsBuilder.AddEEClass("System.String", attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0); systemStringMethodTablePtr = rtsBuilder.AddMethodTable("System.String", mtflags: mtflags, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.StringBaseSize, @@ -216,7 +216,7 @@ public void ValidateArrayInstMethodTable(MockTarget.Architecture arch) module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3); rtsBuilder.SetEEClassAndCanonMTRefs(systemArrayEEClassPtr, systemArrayMethodTablePtr); - const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; + const uint arrayInst_mtflags = (uint)(MethodTableFlags_1.WFLAGS_HIGH.HasComponentSize | MethodTableFlags_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize; const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed); TargetPointer arrayInstanceEEClassPtr = rtsBuilder.AddEEClass("EEClass ArrayInstance", attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index d302d93cd0a79..a0fc554238319 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -6,8 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using System.Text; -using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; namespace Microsoft.Diagnostics.DataContractReader.UnitTests; @@ -447,9 +446,9 @@ internal TargetPointer AddArrayObject(Array array) // ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); //ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); - uint flags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; + uint flags = (uint)(MethodTableFlags_1.WFLAGS_HIGH.HasComponentSize | MethodTableFlags_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; if (isSingleDimensionZeroLowerBound) - flags |= (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.Category_IfArrayThenSzArray; + flags |= (uint)MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray; string name = string.Join(',', array); From 69bd46ff58409a20b317976082efed52c45035f1 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 29 Oct 2024 15:10:59 -0400 Subject: [PATCH 18/20] fixup rebase --- src/native/managed/cdacreader/tests/MockDescriptors.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index a0fc554238319..22d655a180de7 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Text; using System.Runtime.InteropServices; using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; @@ -120,7 +121,7 @@ private static readonly (string, DataType)[] ThreadStoreFields = (nameof(Data.ThreadStore.DeadCount), DataType.uint32), ]; - public static class RuntimeTypeSystem + public class RuntimeTypeSystem { internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; From 014e85d771f0db498b7598fc24ddead7af024ce9 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Tue, 29 Oct 2024 16:45:40 -0400 Subject: [PATCH 19/20] fixup the Thread ExceptionInfo allocation when using a bump allocator with alignment The ExceptionInfo is actually placed right in the Thread object when funclets aren't in use. So we don't want the bump allocator to do 2 separate allocations with a hole in between. (which is what the default 16-byte alignment was leaving) --- .../cdacreader/tests/MockDescriptors.cs | 19 ++++++++++++++----- .../tests/MockMemorySpace.BumpAllocator.cs | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 22d655a180de7..7b90b20990deb 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -540,6 +540,7 @@ internal TargetPointer AddModule(string? path = null, string? fileName = null) public class Thread { + const bool UseFunclets = false; private const ulong DefaultAllocationRangeStart = 0x0003_0000; private const ulong DefaultAllocationRangeEnd = 0x0004_0000; @@ -596,7 +597,7 @@ public Thread(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocati (nameof(Constants.Globals.ThreadStore), threadStoreGlobal.Address, null), (nameof(Constants.Globals.FinalizerThread), finalizerThreadGlobal.Address, null), (nameof(Constants.Globals.GCThread), gcThreadGlobal.Address, null), - (nameof(Constants.Globals.FeatureEHFunclets), 0, null), + (nameof(Constants.Globals.FeatureEHFunclets), UseFunclets ? 1 : 0, null), ]; } @@ -639,7 +640,10 @@ internal TargetPointer AddThread(uint id, TargetNUInt osId) { TargetTestHelpers helpers = _builder.TargetTestHelpers; Target.TypeInfo typeInfo = Types[DataType.Thread]; - MockMemorySpace.HeapFragment thread = _allocator.Allocate(typeInfo.Size.Value, "Thread"); + if (UseFunclets) + throw new NotImplementedException("todo for funclets: allocate the ExceptionInfo separately"); + ulong allocSize = typeInfo.Size.Value + (UseFunclets ? 0 : Types[DataType.ExceptionInfo].Size.Value); + MockMemorySpace.HeapFragment thread = _allocator.Allocate(allocSize, UseFunclets ? "Thread" : "Thread and ExceptionInfo"); Span data = thread.Data.AsSpan(); helpers.Write( data.Slice(typeInfo.Fields[nameof(Data.Thread.Id)].Offset), @@ -650,11 +654,16 @@ internal TargetPointer AddThread(uint id, TargetNUInt osId) _builder.AddHeapFragment(thread); // Add exception info for the thread - MockMemorySpace.HeapFragment exceptionInfo = _allocator.Allocate(Types[DataType.ExceptionInfo].Size.Value, "ExceptionInfo"); - _builder.AddHeapFragment(exceptionInfo); + //MockMemorySpace.HeapFragment exceptionInfo = _allocator.Allocate(Types[DataType.ExceptionInfo].Size.Value, "ExceptionInfo"); + //_builder.AddHeapFragment(exceptionInfo); + TargetPointer exceptionInfoAddress = default; + if (UseFunclets) + throw new NotImplementedException("todo for funclets: allocate and ExceptionInfo"); + else + exceptionInfoAddress = thread.Address + Types[DataType.ExceptionInfo].Size.Value; helpers.WritePointer( data.Slice(typeInfo.Fields[nameof(Data.Thread.ExceptionTracker)].Offset), - exceptionInfo.Address); + exceptionInfoAddress); ulong threadLinkOffset = (ulong)typeInfo.Fields[nameof(Data.Thread.LinkNext)].Offset; if (_previousThread != TargetPointer.Null) diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs index 641d58bd2b96c..17b052c09ca34 100644 --- a/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.BumpAllocator.cs @@ -45,6 +45,8 @@ private ulong AlignUp(ulong value) public bool TryAllocate(ulong size, string name, [NotNullWhen(true)] out HeapFragment? fragment) { ulong current = AlignUp(_current); + Debug.Assert(current >= _current); + Debug.Assert((current % (ulong)MinAlign) == 0); if (current + size <= _blockEnd) { fragment = new HeapFragment { From dc1923cbd2d139f54ae89edefc72194c8a6397af Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 30 Oct 2024 10:23:12 -0400 Subject: [PATCH 20/20] Apply suggestions from code review Co-Authored-By: Elinor Fung --- .../cdacreader/tests/MethodTableTests.cs | 6 ++-- .../cdacreader/tests/MockDescriptors.cs | 36 ++++++++++--------- .../managed/cdacreader/tests/ObjectTests.cs | 6 ++-- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodTableTests.cs b/src/native/managed/cdacreader/tests/MethodTableTests.cs index 3004f5872263f..a2c7ec4c7ba31 100644 --- a/src/native/managed/cdacreader/tests/MethodTableTests.cs +++ b/src/native/managed/cdacreader/tests/MethodTableTests.cs @@ -18,15 +18,13 @@ private static void RTSContractHelper(MockTarget.Architecture arch, Action rtsTypes = new (); - MockRTS rtsBuilder = new (rtsTypes, builder) { + MockRTS rtsBuilder = new (builder) { // arbitrary address range TypeSystemAllocator = builder.CreateAllocator(start: 0x00000000_33000000, end: 0x00000000_34000000), }; - MockRTS.AddTypes(targetTestHelpers, rtsTypes); builder .SetContracts ([ nameof(Contracts.RuntimeTypeSystem) ]) - .SetTypes (rtsTypes) + .SetTypes (rtsBuilder.Types) .SetGlobals (MockRTS.Globals); rtsBuilder.AddGlobalPointers(); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors.cs index 7b90b20990deb..1dc2051338915 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors.cs @@ -125,8 +125,10 @@ public class RuntimeTypeSystem { internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; - internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary types) + private Dictionary GetTypes() { + var targetTestHelpers = Builder.TargetTestHelpers; + Dictionary types = new (); var layout = targetTestHelpers.LayoutFields(MethodTableFields); types[DataType.MethodTable] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; var eeClassLayout = targetTestHelpers.LayoutFields(EEClassFields); @@ -134,6 +136,7 @@ internal static void AddTypes(TargetTestHelpers targetTestHelpers, Dictionary Types; + + internal Dictionary Types { get; init; } internal MockMemorySpace.BumpAllocator TypeSystemAllocator { get; set; } internal TargetPointer FreeObjectMethodTableAddress { get; private set; } - internal RuntimeTypeSystem(Dictionary types, MockMemorySpace.Builder builder) + internal RuntimeTypeSystem(MockMemorySpace.Builder builder) { - Types = types; Builder = builder; + Types = GetTypes();; } internal void AddGlobalPointers() @@ -266,7 +270,6 @@ public class Object internal const ulong TestSyncBlockValueToObjectOffset = sizeof(uint); internal readonly RuntimeTypeSystem RTSBuilder; - internal Dictionary Types => RTSBuilder.Types; internal MockMemorySpace.Builder Builder => RTSBuilder.Builder; internal MockMemorySpace.BumpAllocator ManagedObjectAllocator { get; set; } @@ -275,17 +278,22 @@ public class Object internal TargetPointer TestStringMethodTableAddress { get; private set; } + internal Dictionary Types { get; init; } + internal Object(RuntimeTypeSystem rtsBuilder) { RTSBuilder = rtsBuilder; const ulong TestSyncBlocksAddress = 0x00000000_e0000000; SyncBlockAllocator = Builder.CreateAllocator(start: TestSyncBlocksAddress, end: TestSyncBlocksAddress + 0x1000); + + Types = GetTypes(); } - internal static void AddTypes(Dictionary types, TargetTestHelpers helpers) + private Dictionary GetTypes() { - RuntimeTypeSystem.AddTypes(helpers, types); + var helpers = Builder.TargetTestHelpers; + Dictionary types = RTSBuilder.Types; var objectLayout = helpers.LayoutFields(ObjectFields); types[DataType.Object] = new Target.TypeInfo() { Fields = objectLayout.Fields, Size = objectLayout.Stride }; var layout = helpers.ExtendLayout(StringFields, objectLayout); @@ -299,6 +307,7 @@ internal static void AddTypes(Dictionary types, Targe types[DataType.SyncBlock] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; layout = helpers.LayoutFields(InteropSyncBlockFields); types[DataType.InteropSyncBlockInfo] = new Target.TypeInfo() { Fields = layout.Fields, Size = layout.Stride }; + return types; } internal static (string Name, ulong Value, string? Type)[] Globals(TargetTestHelpers helpers) => RuntimeTypeSystem.Globals.Concat( @@ -444,9 +453,6 @@ internal TargetPointer AddArrayObject(Array array) if (!isSingleDimensionZeroLowerBound) size += array.Rank * sizeof(int) * 2; - // ulong methodTableAddress = (address.Value + (ulong)size + (TestObjectToMethodTableUnmask - 1)) & ~(TestObjectToMethodTableUnmask - 1); - //ulong arrayClassAddress = methodTableAddress + (ulong)targetTestHelpers.SizeOfTypeInfo(types[DataType.MethodTable]); - uint flags = (uint)(MethodTableFlags_1.WFLAGS_HIGH.HasComponentSize | MethodTableFlags_1.WFLAGS_HIGH.Category_Array) | (uint)array.Length; if (isSingleDimensionZeroLowerBound) flags |= (uint)MethodTableFlags_1.WFLAGS_HIGH.Category_IfArrayThenSzArray; @@ -654,13 +660,9 @@ internal TargetPointer AddThread(uint id, TargetNUInt osId) _builder.AddHeapFragment(thread); // Add exception info for the thread - //MockMemorySpace.HeapFragment exceptionInfo = _allocator.Allocate(Types[DataType.ExceptionInfo].Size.Value, "ExceptionInfo"); - //_builder.AddHeapFragment(exceptionInfo); - TargetPointer exceptionInfoAddress = default; - if (UseFunclets) - throw new NotImplementedException("todo for funclets: allocate and ExceptionInfo"); - else - exceptionInfoAddress = thread.Address + Types[DataType.ExceptionInfo].Size.Value; + // Add exception info for the thread + // TODO: [cdac] Handle when UseFunclets is true - see NotImplementedException thrown above + TargetPointer exceptionInfoAddress = thread.Address + Types[DataType.ExceptionInfo].Size.Value; helpers.WritePointer( data.Slice(typeInfo.Fields[nameof(Data.Thread.ExceptionTracker)].Offset), exceptionInfoAddress); diff --git a/src/native/managed/cdacreader/tests/ObjectTests.cs b/src/native/managed/cdacreader/tests/ObjectTests.cs index f76a1978f95a5..ba2e36d6fd9c7 100644 --- a/src/native/managed/cdacreader/tests/ObjectTests.cs +++ b/src/native/managed/cdacreader/tests/ObjectTests.cs @@ -18,8 +18,7 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Action types = new(); - MockDescriptors.RuntimeTypeSystem rtsBuilder = new(types, builder) { + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder) { // arbtrary address range TypeSystemAllocator = builder.CreateAllocator(start: 0x00000000_4a000000, end: 0x00000000_4b000000), }; @@ -27,11 +26,10 @@ private static void ObjectContractHelper(MockTarget.Architecture arch, Action