Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] Handle type descs in RuntimeTypeSystem_1.GetLoaderModule #110325

Merged
merged 2 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -894,8 +894,25 @@ private bool HasMethodInstantiation(MethodDesc md)

private TargetPointer GetLoaderModule(TypeHandle typeHandle)
{
if (!typeHandle.IsMethodTable())
throw new NotImplementedException(); // TODO[cdac]: TypeDesc::GetLoaderModule()
if (typeHandle.IsTypeDesc())
{
// TypeDesc::GetLoaderModule
if (HasTypeParam(typeHandle))
{
return GetLoaderModule(GetTypeParam(typeHandle));
}
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
{
return genericParamModule;
}
else
{
System.Diagnostics.Debug.Assert(IsFunctionPointer(typeHandle, out _, out _));
FnPtrTypeDesc fnPtrTypeDesc = _target.ProcessedData.GetOrAdd<FnPtrTypeDesc>(typeHandle.TypeDescAddress());
return fnPtrTypeDesc.LoaderModule;
}
}

MethodTable mt = _methodTables[typeHandle.Address];
Data.MethodTableAuxiliaryData mtAuxData = _target.ProcessedData.GetOrAdd<Data.MethodTableAuxiliaryData>(mt.AuxiliaryData);
return mtAuxData.LoaderModule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public ParamTypeDesc(Target target, TargetPointer address)
TypeAndFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(TypeAndFlags)].Offset);

type = target.GetTypeInfo(DataType.ParamTypeDesc);
TypeArg = target.Read<ushort>(address + (ulong)type.Fields[nameof(TypeArg)].Offset);
TypeArg = target.ReadPointer(address + (ulong)type.Fields[nameof(TypeArg)].Offset);
}

public uint TypeAndFlags { get; init; }
Expand Down Expand Up @@ -63,10 +63,12 @@ public FnPtrTypeDesc(Target target, TargetPointer address)
NumArgs = target.Read<uint>(address + (ulong)type.Fields[nameof(NumArgs)].Offset);
CallConv = target.Read<uint>(address + (ulong)type.Fields[nameof(CallConv)].Offset);
RetAndArgTypes = (TargetPointer)(address + (ulong)type.Fields[nameof(RetAndArgTypes)].Offset);
LoaderModule = target.ReadPointer(address + (ulong)type.Fields[nameof(LoaderModule)].Offset);
}

public uint TypeAndFlags { get; init; }
public uint NumArgs { get; init; }
public uint CallConv { get; init; }
public TargetPointer RetAndArgTypes { get; init; }
public TargetPointer LoaderModule { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Tests;

Expand All @@ -15,6 +16,49 @@ public class RuntimeTypeSystem
private const ulong DefaultAllocationRangeStart = 0x00000000_4a000000;
private const ulong DefaultAllocationRangeEnd = 0x00000000_4b000000;

private static readonly TypeFields TypeDescFields = new TypeFields()
{
DataType = DataType.TypeDesc,
Fields =
[
new(nameof(Data.TypeDesc.TypeAndFlags), DataType.uint32),
]
};

private static readonly TypeFields FnPtrTypeDescFields = new TypeFields()
{
DataType = DataType.FnPtrTypeDesc,
Fields =
[
new(nameof(Data.FnPtrTypeDesc.NumArgs), DataType.uint32),
new(nameof(Data.FnPtrTypeDesc.CallConv), DataType.uint32),
new(nameof(Data.FnPtrTypeDesc.LoaderModule), DataType.pointer),
new(nameof(Data.FnPtrTypeDesc.RetAndArgTypes), DataType.pointer),
],
BaseTypeFields = TypeDescFields
};

private static readonly TypeFields ParamTypeDescFields = new TypeFields()
{
DataType = DataType.ParamTypeDesc,
Fields =
[
new(nameof(Data.ParamTypeDesc.TypeArg), DataType.pointer),
],
BaseTypeFields = TypeDescFields
};

private static readonly TypeFields TypeVarTypeDescFields = new TypeFields()
{
DataType = DataType.TypeVarTypeDesc,
Fields =
[
new(nameof(Data.TypeVarTypeDesc.Module), DataType.pointer),
new(nameof(Data.TypeVarTypeDesc.Token), DataType.uint32),
],
BaseTypeFields = TypeDescFields
};

private static Dictionary<DataType, Target.TypeInfo> GetTypes(TargetTestHelpers helpers)
{
return GetTypesForTypeFields(
Expand All @@ -24,6 +68,10 @@ public class RuntimeTypeSystem
EEClassFields,
ArrayClassFields,
MethodTableAuxiliaryDataFields,
TypeDescFields,
FnPtrTypeDescFields,
ParamTypeDescFields,
TypeVarTypeDescFields,
]);
}

Expand Down Expand Up @@ -164,5 +212,62 @@ internal void SetMethodTableAuxData(TargetPointer methodTablePointer, TargetPoin
Span<byte> methodTable = Builder.BorrowAddressRange(methodTablePointer, (int)methodTableTypeInfo.Size.Value);
Builder.TargetTestHelpers.WritePointer(methodTable.Slice(methodTableTypeInfo.Fields[nameof(Data.MethodTable.AuxiliaryData)].Offset), auxDataFragment.Address);
}

internal TargetPointer AddFunctionPointerTypeDesc(uint callConv, TargetPointer[] retAndArgTypes, TargetPointer loaderModule)
{
Target.TypeInfo typeInfo = Types[DataType.FnPtrTypeDesc];
TargetTestHelpers helpers = Builder.TargetTestHelpers;

ulong size = typeInfo.Size.Value + (ulong)(retAndArgTypes.Length * helpers.PointerSize);
MockMemorySpace.HeapFragment fragment = TypeSystemAllocator.Allocate(size, $"FnPtrTypeDesc");
Builder.AddHeapFragment(fragment);
Span<byte> dest = fragment.Data;
helpers.Write(dest.Slice(Types[DataType.TypeDesc].Fields[nameof(Data.TypeDesc.TypeAndFlags)].Offset), (uint)CorElementType.FnPtr);
helpers.Write(dest.Slice(typeInfo.Fields[nameof(Data.FnPtrTypeDesc.NumArgs)].Offset), retAndArgTypes.Length - 1);
helpers.Write(dest.Slice(typeInfo.Fields[nameof(Data.FnPtrTypeDesc.CallConv)].Offset), callConv);
helpers.WritePointer(dest.Slice(typeInfo.Fields[nameof(Data.FnPtrTypeDesc.LoaderModule)].Offset), loaderModule);
for (int i = 0; i < retAndArgTypes.Length; i ++)
{
Span<byte> span = fragment.Data.AsSpan().Slice(typeInfo.Fields[nameof(Data.FnPtrTypeDesc.RetAndArgTypes)].Offset + i * helpers.PointerSize, helpers.PointerSize);
helpers.WritePointer(span, retAndArgTypes[i]);
}

return fragment.Address;
}

internal TargetPointer AddParamTypeDesc(uint typeAndFlags, TargetPointer typeArg)
{
CorElementType type = (CorElementType)(typeAndFlags & 0xFF);
if (type != CorElementType.ValueType && type != CorElementType.Byref && type != CorElementType.Ptr)
throw new ArgumentOutOfRangeException(nameof(typeAndFlags));

Target.TypeInfo typeInfo = Types[DataType.ParamTypeDesc];
TargetTestHelpers helpers = Builder.TargetTestHelpers;

MockMemorySpace.HeapFragment fragment = TypeSystemAllocator.Allocate(typeInfo.Size.Value, $"ParamTypeDesc");
Builder.AddHeapFragment(fragment);
Span<byte> dest = fragment.Data;
helpers.Write(dest.Slice(Types[DataType.TypeDesc].Fields[nameof(Data.TypeDesc.TypeAndFlags)].Offset), typeAndFlags);
helpers.WritePointer(dest.Slice(typeInfo.Fields[nameof(Data.ParamTypeDesc.TypeArg)].Offset), typeArg);
return fragment.Address;
}

internal TargetPointer AddTypeVarTypeDesc(uint typeAndFlags, TargetPointer module, uint token)
{
CorElementType type = (CorElementType)(typeAndFlags & 0xFF);
if (type != CorElementType.MVar && type != CorElementType.Var)
throw new ArgumentOutOfRangeException(nameof(typeAndFlags));

Target.TypeInfo typeInfo = Types[DataType.TypeVarTypeDesc];
TargetTestHelpers helpers = Builder.TargetTestHelpers;

MockMemorySpace.HeapFragment fragment = TypeSystemAllocator.Allocate(typeInfo.Size.Value, $"TypeVarTypeDesc");
Builder.AddHeapFragment(fragment);
Span<byte> dest = fragment.Data;
helpers.Write(dest.Slice(Types[DataType.TypeDesc].Fields[nameof(Data.TypeDesc.TypeAndFlags)].Offset), typeAndFlags);
helpers.WritePointer(dest.Slice(typeInfo.Fields[nameof(Data.TypeVarTypeDesc.Module)].Offset), module);
helpers.Write(dest.Slice(typeInfo.Fields[nameof(Data.TypeVarTypeDesc.Token)].Offset), token);
return fragment.Address;
}
}
}
205 changes: 205 additions & 0 deletions src/native/managed/cdacreader/tests/TypeDescTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// 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.Linq;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers;
using Moq;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.Tests;

public class TypeDescTests
{
[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetModule(MockTarget.Architecture arch)
{
TargetTestHelpers helpers = new(arch);
MockMemorySpace.Builder builder = new(helpers);
MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder);

// Arbitrary module value
TargetPointer module = 0xa000;
TargetPointer varType = rtsBuilder.AddTypeVarTypeDesc((uint)CorElementType.Var, module, 0);
TargetPointer typePointerHandle = GetTypeDescHandlePointer(varType);
TargetPointer paramType = rtsBuilder.AddParamTypeDesc((uint)CorElementType.ValueType, typePointerHandle);
TargetPointer funcPtr = rtsBuilder.AddFunctionPointerTypeDesc(0, [GetTypeDescHandlePointer(0x1000)], module);

Target target = CreateTarget(rtsBuilder);
IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem;

{
// Type var type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(varType));
TargetPointer actualModule = rts.GetModule(handle);
Assert.Equal(module, actualModule);
}
{
// Param type - pointing at var type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(paramType));
TargetPointer actualModule = rts.GetModule(handle);
Assert.Equal(module, actualModule);
}
{
// Function pointer - always null
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(funcPtr));
TargetPointer actualModule = rts.GetModule(handle);
Assert.Equal(TargetPointer.Null, actualModule);
}
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetTypeParam(MockTarget.Architecture arch)
{
TargetTestHelpers helpers = new(arch);
MockMemorySpace.Builder builder = new(helpers);
MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder);

// Param types - arbitrary type pointer value
TargetPointer typePointerRawAddr = 0xa000;
TargetPointer typePointerHandle = GetTypeDescHandlePointer(typePointerRawAddr);
TargetPointer paramType = rtsBuilder.AddParamTypeDesc((uint)CorElementType.ValueType, typePointerHandle);

// No type param
TargetPointer funcPtr = rtsBuilder.AddFunctionPointerTypeDesc(0, [GetTypeDescHandlePointer(0x1000)], TargetPointer.Null);
TargetPointer varType = rtsBuilder.AddTypeVarTypeDesc((uint)CorElementType.Var, TargetPointer.Null, 0);

Target target = CreateTarget(rtsBuilder);
IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem;

{
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(paramType));
bool res = rts.HasTypeParam(handle);
Assert.True(res);
TypeHandle typeParam = rts.GetTypeParam(handle);
Assert.Equal(typePointerHandle, typeParam.Address);
Assert.Equal(typePointerRawAddr, typeParam.TypeDescAddress());
}
{
// Function pointer
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(funcPtr));
bool res = rts.HasTypeParam(handle);
Assert.False(res);
}
{
// Type var type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(varType));
bool res = rts.HasTypeParam(handle);
Assert.False(res);
}
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void IsFunctionPointer(MockTarget.Architecture arch)
{
TargetTestHelpers helpers = new(arch);
MockMemorySpace.Builder builder = new(helpers);
MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder);

// Function pointer - arbitrary callconv, types, and module values
byte callConv = 0x9;
TargetPointer[] retAndArgTypesRawAddr = [0x1000, 0x2000, 0x3000];
TargetPointer[] retAndArgTypesHandle = retAndArgTypesRawAddr.Select(a => GetTypeDescHandlePointer(a)).ToArray();
TargetPointer loaderModule = 0xcccc0000;
TargetPointer funcPtr = rtsBuilder.AddFunctionPointerTypeDesc(callConv, retAndArgTypesHandle, loaderModule);

// Non-function pointers
TargetPointer paramType = rtsBuilder.AddParamTypeDesc((uint)CorElementType.ValueType, TargetPointer.Null);
TargetPointer varType = rtsBuilder.AddTypeVarTypeDesc((uint)CorElementType.Var, TargetPointer.Null, 0);

Target target = CreateTarget(rtsBuilder);
IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem;

{
// Function pointer
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(funcPtr));
bool res = rts.IsFunctionPointer(handle, out ReadOnlySpan<TypeHandle> actualRetAndArgTypes, out byte actualCallConv);
Assert.True(res);
Assert.Equal(callConv, actualCallConv);
Assert.Equal(retAndArgTypesHandle.Length, actualRetAndArgTypes.Length);
for (int i = 0; i < retAndArgTypesHandle.Length; i++)
{
Assert.Equal(retAndArgTypesHandle[i], actualRetAndArgTypes[i].Address);
Assert.Equal(retAndArgTypesRawAddr[i], actualRetAndArgTypes[i].TypeDescAddress());
}
}
{
// Param type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(paramType));
bool res = rts.IsFunctionPointer(handle, out _, out _);
Assert.False(res);
}
{
// Type var type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(varType));
bool res = rts.IsFunctionPointer(handle, out _, out _);
Assert.False(res);
}
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void IsGenericVariable(MockTarget.Architecture arch)
{
TargetTestHelpers helpers = new(arch);
MockMemorySpace.Builder builder = new(helpers);
MockDescriptors.RuntimeTypeSystem rtsBuilder = new(builder);

// Type var types - arbitrary module and token values;
TargetPointer module = 0x1000;
uint token = 0xa;
TargetPointer varType = rtsBuilder.AddTypeVarTypeDesc((uint)CorElementType.Var, module, token);
TargetPointer mvarType = rtsBuilder.AddTypeVarTypeDesc((uint)CorElementType.MVar, module, token);

// Non-generic variables
TargetPointer paramType = rtsBuilder.AddParamTypeDesc((uint)CorElementType.ValueType, TargetPointer.Null);
TargetPointer funcPtr = rtsBuilder.AddFunctionPointerTypeDesc(0, [GetTypeDescHandlePointer(0x1000)], TargetPointer.Null);

Target target = CreateTarget(rtsBuilder);
IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem;

{
// Var
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(varType));
bool res = rts.IsGenericVariable(handle, out TargetPointer actualModule, out uint actualToken);
Assert.True(res);
Assert.Equal(module, actualModule);
Assert.Equal(token, actualToken);
}
{
// MVar
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(mvarType));
bool res = rts.IsGenericVariable(handle, out TargetPointer actualModule, out uint actualToken);
Assert.True(res);
Assert.Equal(module, actualModule);
Assert.Equal(token, actualToken);
}
{
// Function pointer
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(funcPtr));
bool res = rts.IsGenericVariable(handle, out _, out _);
Assert.False(res);
}
{
// Param type
TypeHandle handle = rts.GetTypeHandle(GetTypeDescHandlePointer(paramType));
bool res = rts.IsGenericVariable(handle, out _, out _);
Assert.False(res);
}
}

private static Target CreateTarget(MockDescriptors.RuntimeTypeSystem rtsBuilder)
{
var target = new TestPlaceholderTarget(rtsBuilder.Builder.TargetTestHelpers.Arch, rtsBuilder.Builder.GetReadContext().ReadFromTarget, rtsBuilder.Types, rtsBuilder.Globals);
target.SetContracts(Mock.Of<ContractRegistry>(
c => c.RuntimeTypeSystem == ((IContractFactory<IRuntimeTypeSystem>)new RuntimeTypeSystemFactory()).CreateContract(target, 1)));
return target;
}

private static TargetPointer GetTypeDescHandlePointer(TargetPointer addr)
=> addr | (ulong)RuntimeTypeSystem_1.TypeHandleBits.TypeDesc;
}
Loading