From cf1ec686d367cd3b2305e3133a2f348a385139cf Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 9 Dec 2024 14:23:38 -0800 Subject: [PATCH 1/5] Handle non-IL method descs in `RuntimeTypeSystem_1.GetMethodClassificationDataType` --- .../debug/runtimeinfo/datadescriptor.h | 20 +++++++++++++++++++ .../DataType.cs | 5 +++++ .../MethodClassification.cs | 3 ++- .../MethodDescOptionalSlots.cs | 10 +++++----- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index b3894f4efa71d..1b4502daf848d 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -373,6 +373,26 @@ CDAC_TYPE_SIZE(sizeof(DynamicMethodDesc)) CDAC_TYPE_FIELD(DynamicMethodDesc, /*pointer*/, MethodName, cdac_data::MethodName) CDAC_TYPE_END(DynamicMethodDesc) +CDAC_TYPE_BEGIN(ArrayMethodDesc) +CDAC_TYPE_SIZE(sizeof(ArrayMethodDesc)) +CDAC_TYPE_END(ArrayMethodDesc) + +CDAC_TYPE_BEGIN(FCallMethodDesc) +CDAC_TYPE_SIZE(sizeof(FCallMethodDesc)) +CDAC_TYPE_END(FCallMethodDesc) + +CDAC_TYPE_BEGIN(PInvokeMethodDesc) +CDAC_TYPE_SIZE(sizeof(NDirectMethodDesc)) +CDAC_TYPE_END(PInvokeMethodDesc) + +CDAC_TYPE_BEGIN(EEImplMethodDesc) +CDAC_TYPE_SIZE(sizeof(EEImplMethodDesc)) +CDAC_TYPE_END(EEImplMethodDesc) + +CDAC_TYPE_BEGIN(CLRToCOMCallMethodDesc) +CDAC_TYPE_SIZE(sizeof(CLRToCOMCallMethodDesc)) +CDAC_TYPE_END(CLRToCOMCallMethodDesc) + CDAC_TYPE_BEGIN(CodePointer) CDAC_TYPE_SIZE(sizeof(PCODE)) CDAC_TYPE_END(CodePointer) diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index ddcdfbac5789a..ef93b469dcfe5 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -57,6 +57,11 @@ public enum DataType InstantiatedMethodDesc, DynamicMethodDesc, StoredSigMethodDesc, + ArrayMethodDesc, + FCallMethodDesc, + PInvokeMethodDesc, + EEImplMethodDesc, + CLRToCOMCallMethodDesc, RangeSectionMap, RangeSectionFragment, RangeSection, diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodClassification.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodClassification.cs index 4291b13e53cc9..2efb5bf8818de 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodClassification.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodClassification.cs @@ -4,6 +4,7 @@ namespace Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; +// See MethodClassification in src/coreclr/vm/method.hpp internal enum MethodClassification { IL = 0, // IL @@ -12,7 +13,7 @@ internal enum MethodClassification EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) Array = 4, // Array ECall Instantiated = 5, // Instantiated generic methods, including descriptors - // for both shared and unshared code (see InstantiatedMethodDesc) + // for both shared and unshared code (see InstantiatedMethodDesc) ComInterop = 6, Dynamic = 7, // for method desc with no metadata behind } diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodDescOptionalSlots.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodDescOptionalSlots.cs index 4405ce8fc0ff7..81efb5b54115b 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodDescOptionalSlots.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/RuntimeTypeSystemHelpers/MethodDescOptionalSlots.cs @@ -46,12 +46,12 @@ private static uint StartOffset(MethodClassification classification, Target targ DataType type = classification switch { MethodClassification.IL => DataType.MethodDesc, - MethodClassification.FCall => throw new NotImplementedException(), //TODO[cdac]: - MethodClassification.PInvoke => throw new NotImplementedException(), //TODO[cdac]: - MethodClassification.EEImpl => throw new NotImplementedException(), //TODO[cdac]: - MethodClassification.Array => throw new NotImplementedException(), //TODO[cdac]: + MethodClassification.FCall => DataType.FCallMethodDesc, + MethodClassification.PInvoke => DataType.PInvokeMethodDesc, + MethodClassification.EEImpl => DataType.EEImplMethodDesc, + MethodClassification.Array => DataType.ArrayMethodDesc, MethodClassification.Instantiated => DataType.InstantiatedMethodDesc, - MethodClassification.ComInterop => throw new NotImplementedException(), //TODO[cdac]: + MethodClassification.ComInterop => DataType.CLRToCOMCallMethodDesc, MethodClassification.Dynamic => DataType.DynamicMethodDesc, _ => throw new InvalidOperationException($"Unexpected method classification 0x{classification:x2} for MethodDesc") }; From a45886f1b3783535dd22d7a6a5b1b665876f46cb Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 9 Dec 2024 14:24:31 -0800 Subject: [PATCH 2/5] Add tests for IsDynamicMethod --- .../cdacreader/tests/MethodDescTests.cs | 55 +++++++++++++++++++ .../MockDescriptors.MethodDescriptors.cs | 32 +++++++++++ .../cdacreader/tests/MockMemorySpace.cs | 4 ++ 3 files changed, 91 insertions(+) diff --git a/src/native/managed/cdacreader/tests/MethodDescTests.cs b/src/native/managed/cdacreader/tests/MethodDescTests.cs index 3157dcfaa535b..ab8740adca8b0 100644 --- a/src/native/managed/cdacreader/tests/MethodDescTests.cs +++ b/src/native/managed/cdacreader/tests/MethodDescTests.cs @@ -69,6 +69,12 @@ public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch) Assert.False(isCollectible); TargetPointer versioning = rts.GetMethodDescVersioningState(handle); Assert.Equal(TargetPointer.Null, versioning); + + // Method classification - IL method + Assert.False(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.False(rts.IsNoMetadataMethod(handle, out _)); + Assert.False(rts.IsDynamicMethod(handle)); + Assert.False(rts.IsILStub(handle)); } public static IEnumerable StdArchOptionalSlotsData() @@ -135,4 +141,53 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc Assert.Equal(expectedCodeSlotAddr, actualNativeCodeSlotAddr); } } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void IsDynamicMethod(MockTarget.Architecture arch) + { + TargetTestHelpers helpers = new(arch); + MockMemorySpace.Builder builder = new(helpers); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); + MockDescriptors.Loader loaderBuilder = new(builder); + MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); + + ushort numVirtuals = 1; + TargetPointer eeClass = rtsBuilder.AddEEClass(string.Empty, 0, 2, 1); + TargetPointer methodTable = rtsBuilder.AddMethodTable(string.Empty, + mtflags: default, mtflags2: default, baseSize: helpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); + rtsBuilder.SetEEClassAndCanonMTRefs(eeClass, methodTable); + + byte count = 2; + uint methodDescSize = methodDescBuilder.Types[DataType.DynamicMethodDesc].Size.Value; + uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder.MethodDescAlignment; + byte chunkSize = (byte)(count * methodDescSizeByAlignment); + TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count: 1, chunkSize, tokenRange: 0); + + TargetPointer dynamicMethod = methodDescBuilder.SetMethodDesc(chunk, index: 0, slotNum: 0, flags: (ushort)MethodClassification.Dynamic, tokenRemainder: 0); + methodDescBuilder.SetDynamicMethodDesc(dynamicMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsLCGMethod); + TargetPointer ilStubMethod = methodDescBuilder.SetMethodDesc(chunk, index: (byte)methodDescSizeByAlignment, slotNum: 0, flags: (ushort)MethodClassification.Dynamic, tokenRemainder: 0); + methodDescBuilder.SetDynamicMethodDesc(ilStubMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsILStub); + + Target target = CreateTarget(methodDescBuilder); + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + { + var handle = rts.GetMethodDescHandle(dynamicMethod); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.True(rts.IsNoMetadataMethod(handle, out _)); + Assert.True(rts.IsDynamicMethod(handle)); + Assert.False(rts.IsILStub(handle)); + } + { + var handle = rts.GetMethodDescHandle(ilStubMethod); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.True(rts.IsNoMetadataMethod(handle, out _)); + Assert.False(rts.IsDynamicMethod(handle)); + Assert.True(rts.IsILStub(handle)); + } + } } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index 2e77d0238e0ef..e18b3cda92073 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -40,6 +40,28 @@ public class MethodDescriptors ] }; + private static readonly TypeFields StoredSigMethodDescFields = new TypeFields() + { + DataType = DataType.StoredSigMethodDesc, + Fields = + [ + new(nameof(Data.StoredSigMethodDesc.Sig), DataType.pointer), + new(nameof(Data.StoredSigMethodDesc.cSig), DataType.uint32), + new(nameof(Data.StoredSigMethodDesc.ExtendedFlags), DataType.uint32), + ], + BaseTypeFields = MethodDescFields + }; + + private static readonly TypeFields DynamicMethodDescFields = new TypeFields() + { + DataType = DataType.DynamicMethodDesc, + Fields = + [ + new(nameof(Data.DynamicMethodDesc.MethodName), DataType.pointer), + ], + BaseTypeFields = StoredSigMethodDescFields + }; + private const ulong DefaultAllocationRangeStart = 0x2000_2000; private const ulong DefaultAllocationRangeEnd = 0x2000_3000; @@ -78,6 +100,8 @@ internal MethodDescriptors(RuntimeTypeSystem rtsBuilder, Loader loaderBuilder, ( [ MethodDescFields, MethodDescChunkFields, + StoredSigMethodDescFields, + DynamicMethodDescFields, ]); types[DataType.NonVtableSlot] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize }; types[DataType.MethodImpl] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize * 2 }; @@ -121,5 +145,13 @@ internal TargetPointer SetMethodDesc(TargetPointer methodDescChunk, byte index, TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.Slot)].Offset), slotNum); return methodDesc; } + + internal TargetPointer SetDynamicMethodDesc(TargetPointer methodDesc, uint extendedFlags) + { + Target.TypeInfo storedSigInfo = Types[DataType.StoredSigMethodDesc]; + Span data = Builder.BorrowAddressRange(methodDesc, (int)storedSigInfo.Size.Value); + TargetTestHelpers.Write(data.Slice(storedSigInfo.Fields[nameof(Data.StoredSigMethodDesc.ExtendedFlags)].Offset), extendedFlags); + return methodDesc; + } } } diff --git a/src/native/managed/cdacreader/tests/MockMemorySpace.cs b/src/native/managed/cdacreader/tests/MockMemorySpace.cs index a6a7e26781a1e..364a43b6c3ddb 100644 --- a/src/native/managed/cdacreader/tests/MockMemorySpace.cs +++ b/src/native/managed/cdacreader/tests/MockMemorySpace.cs @@ -120,8 +120,12 @@ internal class ReadContext internal int ReadFromTarget(ulong address, Span buffer) { + if (buffer.Length == 0) + return 0; + if (address == 0) return -1; + bool partialReadOcurred = false; HeapFragment lastHeapFragment = default; int availableLength = 0; From 2d9b2d98d1217584720ddfa19db2f8d26e024745 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 9 Dec 2024 18:36:58 -0800 Subject: [PATCH 3/5] Add tests for IsArrayMethod and IsGenericMethodDefinition --- .../cdacreader/tests/MethodDescTests.cs | 214 +++++++++++++----- .../MockDescriptors.MethodDescriptors.cs | 36 ++- 2 files changed, 197 insertions(+), 53 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodDescTests.cs b/src/native/managed/cdacreader/tests/MethodDescTests.cs index ab8740adca8b0..a39f0157c81af 100644 --- a/src/native/managed/cdacreader/tests/MethodDescTests.cs +++ b/src/native/managed/cdacreader/tests/MethodDescTests.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DataContractReader.Contracts; using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; using Moq; @@ -24,7 +26,7 @@ private static Target CreateTarget(MockDescriptors.MethodDescriptors methodDescB [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch) + public void GetMethodDescHandle_ILMethod_GetBasicData(MockTarget.Architecture arch) { TargetTestHelpers helpers = new(arch); MockMemorySpace.Builder builder = new(helpers); @@ -75,6 +77,157 @@ public void MethodDescGetMethodDescTokenOk(MockTarget.Architecture arch) Assert.False(rts.IsNoMetadataMethod(handle, out _)); Assert.False(rts.IsDynamicMethod(handle)); Assert.False(rts.IsILStub(handle)); + Assert.False(rts.IsArrayMethod(handle, out _)); + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void IsArrayMethod(MockTarget.Architecture arch) + { + TargetTestHelpers helpers = new(arch); + MockMemorySpace.Builder builder = new(helpers); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); + MockDescriptors.Loader loaderBuilder = new(builder); + MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); + + ushort numVirtuals = 1; + TargetPointer methodTable = AddMethodTable(rtsBuilder, numVirtuals); + + byte count = 5; + uint methodDescSize = methodDescBuilder.Types[DataType.ArrayMethodDesc].Size.Value; + uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder.MethodDescAlignment; + byte chunkSize = (byte)(count * methodDescSizeByAlignment); + TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count, chunkSize, tokenRange: 0); + + TargetPointer[] arrayMethods = new TargetPointer[count]; + for (byte i = 0; i < count; i++) + { + // Add the array methods by setting the appropriate slot number + // Array vtable is: + // + // Get + // Set + // Address + // .ctor + // [optionally other constructors] + byte index = (byte)(i * methodDescSizeByAlignment); + ushort slotNum = (ushort)(numVirtuals + i); + ushort flags = (ushort)MethodClassification.Array | (ushort)MethodDescFlags_1.MethodDescFlags.HasNonVtableSlot; + arrayMethods[i] = methodDescBuilder.SetMethodDesc(chunk, index, slotNum, flags, tokenRemainder: 0); + } + + Target target = CreateTarget(methodDescBuilder); + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + for (byte i = 0; i < count; i++) + { + MethodDescHandle handle = rts.GetMethodDescHandle(arrayMethods[i]); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.True(rts.IsArrayMethod(handle, out ArrayFunctionType functionType)); + + ArrayFunctionType expectedFunctionType = i <= (byte)ArrayFunctionType.Constructor + ? (ArrayFunctionType)i + : ArrayFunctionType.Constructor; + Assert.Equal(expectedFunctionType, functionType); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void IsDynamicMethod(MockTarget.Architecture arch) + { + TargetTestHelpers helpers = new(arch); + MockMemorySpace.Builder builder = new(helpers); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); + MockDescriptors.Loader loaderBuilder = new(builder); + MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); + + TargetPointer methodTable = AddMethodTable(rtsBuilder); + + byte count = 2; + uint methodDescSize = methodDescBuilder.Types[DataType.DynamicMethodDesc].Size.Value; + uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder.MethodDescAlignment; + byte chunkSize = (byte)(count * methodDescSizeByAlignment); + TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count, chunkSize, tokenRange: 0); + + ushort flags = (ushort)MethodClassification.Dynamic; + TargetPointer dynamicMethod = methodDescBuilder.SetMethodDesc(chunk, index: 0, slotNum: 0, flags, tokenRemainder: 0); + methodDescBuilder.SetDynamicMethodDesc(dynamicMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsLCGMethod); + TargetPointer ilStubMethod = methodDescBuilder.SetMethodDesc(chunk, index: (byte)methodDescSizeByAlignment, slotNum: 1, flags, tokenRemainder: 0); + methodDescBuilder.SetDynamicMethodDesc(ilStubMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsILStub); + + Target target = CreateTarget(methodDescBuilder); + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + { + MethodDescHandle handle = rts.GetMethodDescHandle(dynamicMethod); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.True(rts.IsNoMetadataMethod(handle, out _)); + Assert.True(rts.IsDynamicMethod(handle)); + Assert.False(rts.IsILStub(handle)); + } + { + MethodDescHandle handle = rts.GetMethodDescHandle(ilStubMethod); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); + Assert.True(rts.IsNoMetadataMethod(handle, out _)); + Assert.False(rts.IsDynamicMethod(handle)); + Assert.True(rts.IsILStub(handle)); + } + } + + [Theory] + [ClassData(typeof(MockTarget.StdArch))] + public void IsGenericMethodDefinition(MockTarget.Architecture arch) + { + TargetTestHelpers helpers = new(arch); + MockMemorySpace.Builder builder = new(helpers); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); + MockDescriptors.Loader loaderBuilder = new(builder); + MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); + + TargetPointer methodTable = AddMethodTable(rtsBuilder); + + byte count = 2; + uint methodDescSize = methodDescBuilder.Types[DataType.InstantiatedMethodDesc].Size.Value; + uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder.MethodDescAlignment; + byte chunkSize = (byte)(count * methodDescSizeByAlignment); + TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count, chunkSize, tokenRange: 0); + + ushort flags = (ushort)MethodClassification.Instantiated; + TargetPointer genericMethodDef = methodDescBuilder.SetMethodDesc(chunk, index: 0, slotNum: 0, flags, tokenRemainder: 0); + methodDescBuilder.SetInstantiatedMethodDesc(genericMethodDef, (ushort)RuntimeTypeSystem_1.InstantiatedMethodDescFlags2.GenericMethodDefinition, []); + TargetPointer[] typeArgsRawAddrs = [0x1000, 0x2000, 0x3000]; + TargetPointer[] typeArgsHandles = typeArgsRawAddrs.Select(a => GetTypeDescHandlePointer(a)).ToArray(); + + TargetPointer genericWithInst = methodDescBuilder.SetMethodDesc(chunk, index: (byte)methodDescSizeByAlignment, slotNum: 1, flags, tokenRemainder: 0); + methodDescBuilder.SetInstantiatedMethodDesc(genericWithInst, (ushort)RuntimeTypeSystem_1.InstantiatedMethodDescFlags2.GenericMethodDefinition, typeArgsHandles); + + Target target = CreateTarget(methodDescBuilder); + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + { + MethodDescHandle handle = rts.GetMethodDescHandle(genericMethodDef); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsGenericMethodDefinition(handle)); + ReadOnlySpan instantiation = rts.GetGenericMethodInstantiation(handle); + Assert.Equal(0, instantiation.Length); + } + + { + MethodDescHandle handle = rts.GetMethodDescHandle(genericWithInst); + Assert.NotEqual(TargetPointer.Null, handle.Address); + Assert.True(rts.IsGenericMethodDefinition(handle)); + ReadOnlySpan instantiation = rts.GetGenericMethodInstantiation(handle); + Assert.Equal(typeArgsRawAddrs.Length, instantiation.Length); + for (int i = 0; i < typeArgsRawAddrs.Length; i++) + { + Assert.Equal(typeArgsHandles[i], instantiation[i].Address); + Assert.Equal(typeArgsRawAddrs[i], instantiation[i].TypeDescAddress()); + } + } } public static IEnumerable StdArchOptionalSlotsData() @@ -104,12 +257,7 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); MethodDescFlags_1.MethodDescFlags flags = (MethodDescFlags_1.MethodDescFlags)flagsValue; - ushort numVirtuals = 1; - TargetPointer eeClass = rtsBuilder.AddEEClass(string.Empty, 0, 2, 1); - TargetPointer methodTable = rtsBuilder.AddMethodTable(string.Empty, - mtflags: default, mtflags2: default, baseSize: helpers.ObjectBaseSize, - module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); - rtsBuilder.SetEEClassAndCanonMTRefs(eeClass, methodTable); + TargetPointer methodTable = AddMethodTable(rtsBuilder); uint methodDescSize = methodDescBuilder.Types[DataType.MethodDesc].Size.Value; if (flags.HasFlag(MethodDescFlags_1.MethodDescFlags.HasNonVtableSlot)) @@ -142,52 +290,16 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc } } - [Theory] - [ClassData(typeof(MockTarget.StdArch))] - public void IsDynamicMethod(MockTarget.Architecture arch) + private TargetPointer AddMethodTable(MockDescriptors.RuntimeTypeSystem rtsBuilder, ushort numVirtuals = 5) { - TargetTestHelpers helpers = new(arch); - MockMemorySpace.Builder builder = new(helpers); - MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); - MockDescriptors.Loader loaderBuilder = new(builder); - MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); - - ushort numVirtuals = 1; - TargetPointer eeClass = rtsBuilder.AddEEClass(string.Empty, 0, 2, 1); + TargetPointer eeClass = rtsBuilder.AddEEClass(string.Empty, attr: 0, numMethods: 2, numNonVirtualSlots: 1); TargetPointer methodTable = rtsBuilder.AddMethodTable(string.Empty, - mtflags: default, mtflags2: default, baseSize: helpers.ObjectBaseSize, - module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals); + mtflags: default, mtflags2: default, baseSize: rtsBuilder.Builder.TargetTestHelpers.ObjectBaseSize, + module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals); rtsBuilder.SetEEClassAndCanonMTRefs(eeClass, methodTable); - - byte count = 2; - uint methodDescSize = methodDescBuilder.Types[DataType.DynamicMethodDesc].Size.Value; - uint methodDescSizeByAlignment = methodDescSize / methodDescBuilder.MethodDescAlignment; - byte chunkSize = (byte)(count * methodDescSizeByAlignment); - TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count: 1, chunkSize, tokenRange: 0); - - TargetPointer dynamicMethod = methodDescBuilder.SetMethodDesc(chunk, index: 0, slotNum: 0, flags: (ushort)MethodClassification.Dynamic, tokenRemainder: 0); - methodDescBuilder.SetDynamicMethodDesc(dynamicMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsLCGMethod); - TargetPointer ilStubMethod = methodDescBuilder.SetMethodDesc(chunk, index: (byte)methodDescSizeByAlignment, slotNum: 0, flags: (ushort)MethodClassification.Dynamic, tokenRemainder: 0); - methodDescBuilder.SetDynamicMethodDesc(ilStubMethod, (uint)RuntimeTypeSystem_1.DynamicMethodDescExtendedFlags.IsILStub); - - Target target = CreateTarget(methodDescBuilder); - IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; - - { - var handle = rts.GetMethodDescHandle(dynamicMethod); - Assert.NotEqual(TargetPointer.Null, handle.Address); - Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); - Assert.True(rts.IsNoMetadataMethod(handle, out _)); - Assert.True(rts.IsDynamicMethod(handle)); - Assert.False(rts.IsILStub(handle)); - } - { - var handle = rts.GetMethodDescHandle(ilStubMethod); - Assert.NotEqual(TargetPointer.Null, handle.Address); - Assert.True(rts.IsStoredSigMethodDesc(handle, out _)); - Assert.True(rts.IsNoMetadataMethod(handle, out _)); - Assert.False(rts.IsDynamicMethod(handle)); - Assert.True(rts.IsILStub(handle)); - } + return methodTable; } + + private static TargetPointer GetTypeDescHandlePointer(TargetPointer addr) + => addr | (ulong)RuntimeTypeSystem_1.TypeHandleBits.TypeDesc; } diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index e18b3cda92073..fd3853e714423 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -40,6 +40,18 @@ public class MethodDescriptors ] }; + private static readonly TypeFields InstantiatedMethodDescFields = new TypeFields() + { + DataType = DataType.InstantiatedMethodDesc, + Fields = + [ + new(nameof(Data.InstantiatedMethodDesc.PerInstInfo), DataType.pointer), + new(nameof(Data.InstantiatedMethodDesc.NumGenericArgs), DataType.uint16), + new(nameof(Data.InstantiatedMethodDesc.Flags2), DataType.uint16), + ], + BaseTypeFields = MethodDescFields + }; + private static readonly TypeFields StoredSigMethodDescFields = new TypeFields() { DataType = DataType.StoredSigMethodDesc, @@ -100,12 +112,14 @@ internal MethodDescriptors(RuntimeTypeSystem rtsBuilder, Loader loaderBuilder, ( [ MethodDescFields, MethodDescChunkFields, + InstantiatedMethodDescFields, StoredSigMethodDescFields, DynamicMethodDescFields, ]); types[DataType.NonVtableSlot] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize }; types[DataType.MethodImpl] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize * 2 }; types[DataType.NativeCodeSlot] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize }; + types[DataType.ArrayMethodDesc] = new Target.TypeInfo() { Size = types[DataType.StoredSigMethodDesc].Size.Value }; types = types .Concat(RTSBuilder.Types) .Concat(LoaderBuilder.Types) @@ -146,12 +160,30 @@ internal TargetPointer SetMethodDesc(TargetPointer methodDescChunk, byte index, return methodDesc; } - internal TargetPointer SetDynamicMethodDesc(TargetPointer methodDesc, uint extendedFlags) + internal void SetDynamicMethodDesc(TargetPointer methodDesc, uint extendedFlags) { Target.TypeInfo storedSigInfo = Types[DataType.StoredSigMethodDesc]; Span data = Builder.BorrowAddressRange(methodDesc, (int)storedSigInfo.Size.Value); TargetTestHelpers.Write(data.Slice(storedSigInfo.Fields[nameof(Data.StoredSigMethodDesc.ExtendedFlags)].Offset), extendedFlags); - return methodDesc; + } + + internal void SetInstantiatedMethodDesc(TargetPointer methodDesc, ushort flags, TargetPointer[] typesArgs) + { + Target.TypeInfo typeInfo = Types[DataType.InstantiatedMethodDesc]; + Span data = Builder.BorrowAddressRange(methodDesc, (int)typeInfo.Size.Value); + TargetTestHelpers.Write(data.Slice(typeInfo.Fields[nameof(Data.InstantiatedMethodDesc.Flags2)].Offset), flags); + TargetTestHelpers.Write(data.Slice(typeInfo.Fields[nameof(Data.InstantiatedMethodDesc.NumGenericArgs)].Offset), (ushort)typesArgs.Length); + if (typesArgs.Length > 0) + { + MockMemorySpace.HeapFragment fragment = _allocator.Allocate((ulong)(typesArgs.Length * TargetTestHelpers.PointerSize), "InstantiatedMethodDesc type args"); + Builder.AddHeapFragment(fragment); + TargetTestHelpers.WritePointer(data.Slice(typeInfo.Fields[nameof(Data.InstantiatedMethodDesc.PerInstInfo)].Offset), fragment.Address); + for (int i = 0; i < typesArgs.Length; i++) + { + Span span = fragment.Data.AsSpan().Slice(i * TargetTestHelpers.PointerSize); + TargetTestHelpers.WritePointer(span, typesArgs[i]); + } + } } } } From 979ac38808d44e61527b250c7ac195a2bc445842 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 9 Dec 2024 20:40:01 -0800 Subject: [PATCH 4/5] Add tests for GetNativeCode with different MethodClassification --- .../cdacreader/tests/MethodDescTests.cs | 74 ++++++++++++++++++- .../MockDescriptors.MethodDescriptors.cs | 10 ++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdacreader/tests/MethodDescTests.cs b/src/native/managed/cdacreader/tests/MethodDescTests.cs index a39f0157c81af..c9b675e8ec75e 100644 --- a/src/native/managed/cdacreader/tests/MethodDescTests.cs +++ b/src/native/managed/cdacreader/tests/MethodDescTests.cs @@ -13,14 +13,17 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests; public class MethodDescTests { - private static Target CreateTarget(MockDescriptors.MethodDescriptors methodDescBuilder) + private static Target CreateTarget(MockDescriptors.MethodDescriptors methodDescBuilder, Mock mockExecutionManager = null) { MockMemorySpace.Builder builder = methodDescBuilder.Builder; var target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, methodDescBuilder.Types, methodDescBuilder.Globals); + + mockExecutionManager ??= new Mock(); target.SetContracts(Mock.Of( c => c.RuntimeTypeSystem == ((IContractFactory)new RuntimeTypeSystemFactory()).CreateContract(target, 1) && c.Loader == ((IContractFactory)new LoaderFactory()).CreateContract(target, 1) - && c.PlatformMetadata == new Mock().Object)); + && c.PlatformMetadata == new Mock().Object + && c.ExecutionManager == mockExecutionManager.Object)); return target; } @@ -290,6 +293,73 @@ public void GetAddressOfNativeCodeSlot_OptionalSlots(MockTarget.Architecture arc } } + public static IEnumerable StdArchMethodDescTypeData() + { + foreach (object[] arr in new MockTarget.StdArch()) + { + MockTarget.Architecture arch = (MockTarget.Architecture)arr[0]; + yield return new object[] { arch, DataType.MethodDesc }; + yield return new object[] { arch, DataType.FCallMethodDesc }; + yield return new object[] { arch, DataType.PInvokeMethodDesc }; + yield return new object[] { arch, DataType.EEImplMethodDesc }; + yield return new object[] { arch, DataType.ArrayMethodDesc }; + yield return new object[] { arch, DataType.InstantiatedMethodDesc }; + yield return new object[] { arch, DataType.CLRToCOMCallMethodDesc }; + yield return new object[] { arch, DataType.DynamicMethodDesc }; + } + } + + [Theory] + [MemberData(nameof(StdArchMethodDescTypeData))] + public void GetNativeCode_StableEntryPoint_NonVtableSlot(MockTarget.Architecture arch, DataType methodDescType) + { + TargetTestHelpers helpers = new(arch); + MockMemorySpace.Builder builder = new(helpers); + MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder); + MockDescriptors.Loader loaderBuilder = new(builder); + MockDescriptors.MethodDescriptors methodDescBuilder = new(rtsBuilder, loaderBuilder); + + TargetPointer methodTable = AddMethodTable(rtsBuilder); + MethodClassification classification = methodDescType switch + { + DataType.MethodDesc => MethodClassification.IL, + DataType.FCallMethodDesc => MethodClassification.FCall, + DataType.PInvokeMethodDesc => MethodClassification.PInvoke, + DataType.EEImplMethodDesc => MethodClassification.EEImpl, + DataType.ArrayMethodDesc => MethodClassification.Array, + DataType.InstantiatedMethodDesc => MethodClassification.Instantiated, + DataType.CLRToCOMCallMethodDesc => MethodClassification.ComInterop, + DataType.DynamicMethodDesc => MethodClassification.Dynamic, + _ => throw new ArgumentOutOfRangeException(nameof(methodDescType)) + }; + uint methodDescBaseSize = methodDescBuilder.Types[methodDescType].Size.Value; + uint methodDescSize = methodDescBaseSize + methodDescBuilder.Types[DataType.NonVtableSlot].Size!.Value; + byte chunkSize = (byte)(methodDescSize / methodDescBuilder.MethodDescAlignment); + TargetPointer chunk = methodDescBuilder.AddMethodDescChunk(methodTable, string.Empty, count: 1, chunkSize, tokenRange: 0); + + ushort flags = (ushort)((ushort)classification | (ushort)MethodDescFlags_1.MethodDescFlags.HasNonVtableSlot); + TargetPointer methodDescAddress = methodDescBuilder.SetMethodDesc(chunk, index: 0, slotNum: 0, flags, tokenRemainder: 0, flags3: (ushort)MethodDescFlags_1.MethodDescFlags3.HasStableEntryPoint); + TargetCodePointer nativeCode = new TargetCodePointer(0x0789_abc0); + helpers.WritePointer( + methodDescBuilder.Builder.BorrowAddressRange(methodDescAddress + methodDescBaseSize, helpers.PointerSize), + nativeCode); + + Mock mockExecutionManager = new(); + CodeBlockHandle codeBlock = new CodeBlockHandle(methodDescAddress); + mockExecutionManager.Setup(em => em.GetCodeBlockHandle(nativeCode)) + .Returns(codeBlock); + mockExecutionManager.Setup(em => em.GetMethodDesc(codeBlock)) + .Returns(methodDescAddress); + Target target = CreateTarget(methodDescBuilder, mockExecutionManager); + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + var handle = rts.GetMethodDescHandle(methodDescAddress); + Assert.NotEqual(TargetPointer.Null, handle.Address); + + TargetCodePointer actualNativeCode = rts.GetNativeCode(handle); + Assert.Equal(nativeCode, actualNativeCode); + } + private TargetPointer AddMethodTable(MockDescriptors.RuntimeTypeSystem rtsBuilder, ushort numVirtuals = 5) { TargetPointer eeClass = rtsBuilder.AddEEClass(string.Empty, attr: 0, numMethods: 2, numNonVirtualSlots: 1); diff --git a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs index fd3853e714423..4ce2bb647fa01 100644 --- a/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs +++ b/src/native/managed/cdacreader/tests/MockDescriptors/MockDescriptors.MethodDescriptors.cs @@ -120,6 +120,10 @@ internal MethodDescriptors(RuntimeTypeSystem rtsBuilder, Loader loaderBuilder, ( types[DataType.MethodImpl] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize * 2 }; types[DataType.NativeCodeSlot] = new Target.TypeInfo() { Size = (uint)TargetTestHelpers.PointerSize }; types[DataType.ArrayMethodDesc] = new Target.TypeInfo() { Size = types[DataType.StoredSigMethodDesc].Size.Value }; + types[DataType.FCallMethodDesc] = new Target.TypeInfo() { Size = types[DataType.MethodDesc].Size.Value + (uint)TargetTestHelpers.PointerSize }; + types[DataType.PInvokeMethodDesc] = new Target.TypeInfo() { Size = types[DataType.MethodDesc].Size.Value + (uint)TargetTestHelpers.PointerSize }; + types[DataType.EEImplMethodDesc] = new Target.TypeInfo() { Size = types[DataType.StoredSigMethodDesc].Size.Value }; + types[DataType.CLRToCOMCallMethodDesc] = new Target.TypeInfo() { Size = types[DataType.MethodDesc].Size.Value + (uint)TargetTestHelpers.PointerSize }; types = types .Concat(RTSBuilder.Types) .Concat(LoaderBuilder.Types) @@ -148,14 +152,14 @@ private TargetPointer GetMethodDescAddress(TargetPointer chunkAddress, byte inde return chunkAddress + methodDescChunkTypeInfo.Size.Value + index * MethodDescAlignment; } - internal TargetPointer SetMethodDesc(TargetPointer methodDescChunk, byte index, ushort slotNum, ushort flags, ushort tokenRemainder) + internal TargetPointer SetMethodDesc(TargetPointer methodDescChunk, byte index, ushort slotNum, ushort flags, ushort tokenRemainder, ushort flags3 = 0) { TargetPointer methodDesc = GetMethodDescAddress(methodDescChunk, index); Target.TypeInfo methodDescTypeInfo = Types[DataType.MethodDesc]; Span data = Builder.BorrowAddressRange(methodDesc, (int)methodDescTypeInfo.Size.Value); - TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.ChunkIndex)].Offset), (byte)index); + TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.ChunkIndex)].Offset), index); TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.Flags)].Offset), flags); - TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.Flags3AndTokenRemainder)].Offset), tokenRemainder); + TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.Flags3AndTokenRemainder)].Offset), (ushort)(tokenRemainder | flags3)); TargetTestHelpers.Write(data.Slice(methodDescTypeInfo.Fields[nameof(Data.MethodDesc.Slot)].Offset), slotNum); return methodDesc; } From 92c886dad2cefd81995e91a9efef259a773c2fda Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 11 Dec 2024 13:42:40 -0800 Subject: [PATCH 5/5] Fix non-Windows build --- src/coreclr/debug/runtimeinfo/datadescriptor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 1b4502daf848d..424a1a764039f 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -389,9 +389,11 @@ CDAC_TYPE_BEGIN(EEImplMethodDesc) CDAC_TYPE_SIZE(sizeof(EEImplMethodDesc)) CDAC_TYPE_END(EEImplMethodDesc) +#ifdef FEATURE_COMINTEROP CDAC_TYPE_BEGIN(CLRToCOMCallMethodDesc) CDAC_TYPE_SIZE(sizeof(CLRToCOMCallMethodDesc)) CDAC_TYPE_END(CLRToCOMCallMethodDesc) +#endif // FEATURE_COMINTEROP CDAC_TYPE_BEGIN(CodePointer) CDAC_TYPE_SIZE(sizeof(PCODE))