Skip to content
Open
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 @@ -9,6 +9,7 @@ public static class CorDbgHResults
public const int CORDBG_E_BAD_THREAD_STATE = unchecked((int)0x8013132d);
public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49);
public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
public const int ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A);
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
public const int CORDBG_E_FUNCTION_NOT_IL = unchecked((int)0x8013130a);
public const int CORDBG_E_TARGET_INCONSISTENT = unchecked((int)0x80131c36);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@

using System;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

[GeneratedComClass]
public sealed unsafe partial class ClrDataTypeDefinition : IXCLRDataTypeDefinition
{
private readonly Target _target;
private readonly TargetPointer _module;
private readonly uint _token;
private readonly ITypeHandle? _typeHandle;
private readonly IXCLRDataTypeDefinition? _legacyImpl;

public ClrDataTypeDefinition(Target target, IXCLRDataTypeDefinition? legacyImpl)
public ClrDataTypeDefinition(
Target target,
TargetPointer module,
uint token,
ITypeHandle? typeHandle,
IXCLRDataTypeDefinition? legacyImpl)
{
_target = target;
_module = module;
_token = token;
_typeHandle = typeHandle;
_legacyImpl = legacyImpl;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

[GeneratedComClass]
public sealed unsafe partial class ClrDataTypeInstance : IXCLRDataTypeInstance
{
private readonly Target _target;
private readonly ITypeHandle _typeHandle;
private readonly IXCLRDataTypeInstance? _legacyImpl;

public ClrDataTypeInstance(Target target, IXCLRDataTypeInstance? legacyImpl)
public ClrDataTypeInstance(Target target, ITypeHandle typeHandle, IXCLRDataTypeInstance? legacyImpl)
{
_target = target;
_typeHandle = typeHandle;
_legacyImpl = legacyImpl;
}

Expand Down Expand Up @@ -58,13 +62,124 @@ int IXCLRDataTypeInstance.GetTypeArgumentByIndex(uint index, DacComNullableByRef
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetTypeArgumentByIndex(index, typeArg) : HResults.E_NOTIMPL;

int IXCLRDataTypeInstance.GetName(uint flags, uint bufLen, uint* nameLen, char* nameBuf)
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetName(flags, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;

try
{
if (flags != 0)
throw new ArgumentException("GetName requires flags=0.", nameof(flags));

string name = _typeHandle.GetName(_target);
OutputBufferHelpers.CopyStringToBuffer(nameBuf, bufLen, nameLen, name, out bool truncated);
if (truncated)
{
hr = CorDbgHResults.ERROR_INSUFFICIENT_BUFFER;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (LegacyFallbackHelper.CanFallback() && _legacyImpl is not null)
{
uint nameLenLocal = 0;
char[] nameBufLocal = new char[bufLen > 0 ? bufLen : 1];
int hrLocal;
fixed (char* pNameBufLocal = nameBufLocal)
{
hrLocal = _legacyImpl.GetName(flags, bufLen, &nameLenLocal, nameBuf is null ? null : pNameBufLocal);
}

Debug.ValidateHResult(hr, hrLocal);
if (hr >= 0)
{
string nameLenMessage = nameLen is null
? $"cDAC: <null>, DAC: {nameLenLocal}"
: $"cDAC: {*nameLen}, DAC: {nameLenLocal}";
Debug.Assert(nameLen is null || nameLenLocal == *nameLen, nameLenMessage);

if (nameBuf is not null && nameLenLocal > 0)
{
string dacName = new string(nameBufLocal, 0, (int)nameLenLocal - 1);
string cdacName = new string(nameBuf, 0, (int)nameLenLocal - 1);
Debug.Assert(dacName == cdacName, $"cDAC: {cdacName}, DAC: {dacName}");
}
}
}
#endif

return hr;
}

int IXCLRDataTypeInstance.GetModule(DacComNullableByRef<IXCLRDataModule> mod)
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetModule(mod) : HResults.E_NOTIMPL;

int IXCLRDataTypeInstance.GetDefinition(DacComNullableByRef<IXCLRDataTypeDefinition> typeDefinition)
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetDefinition(typeDefinition) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
int hrLocal = HResults.S_OK;
IXCLRDataTypeDefinition? legacyDefinition = null;
if (LegacyFallbackHelper.CanFallback() && _legacyImpl is not null && !typeDefinition.IsNullRef)
{
DacComNullableByRef<IXCLRDataTypeDefinition> legacyDefinitionOut = new(isNullRef: false);
hrLocal = _legacyImpl.GetDefinition(legacyDefinitionOut);
legacyDefinition = legacyDefinitionOut.Interface;
}

try
{
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
ITypeHandle? definitionType = null;
TargetPointer module = default;
uint token = 0;

if (rts.IsArray(_typeHandle, out _) || rts.IsFunctionPointer(_typeHandle, out _, out _))
{
definitionType = _typeHandle;
module = rts.GetModule(definitionType);
token = rts.GetTypeDefToken(definitionType);
}
else if (rts.IsTypeDesc(_typeHandle) && rts.HasTypeParam(_typeHandle))
{
definitionType = rts.GetTypeParam(_typeHandle);
module = rts.GetModule(definitionType);
token = rts.GetTypeDefToken(definitionType);
}
else
{
module = rts.GetModule(_typeHandle);
token = rts.GetTypeDefToken(_typeHandle);
ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
ModuleLookupTables tables = loader.GetLookupTables(moduleHandle);
TargetPointer definitionTypeAddress = loader.GetModuleLookupMapElement(tables.TypeDefToMethodTable, token, out _);
definitionType = definitionTypeAddress == TargetPointer.Null ? null : rts.GetTypeHandle(definitionTypeAddress);
}

typeDefinition.Interface = new ClrDataTypeDefinition(
_target,
module,
token,
definitionType,
legacyDefinition);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (LegacyFallbackHelper.CanFallback() && _legacyImpl is not null && !typeDefinition.IsNullRef)
{
Debug.ValidateHResult(hr, hrLocal);
}
#endif

return hr;
}

int IXCLRDataTypeInstance.GetFlags(uint* flags)
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal static unsafe class CdacStressApi
public const uint RequestComputeArgGCRefMap = 0xf2000001;

// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER).
private const int HResultErrorInsufficientBuffer = unchecked((int)0x8007007A);
private const int ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A);

public static bool IsStressRequest(uint reqCode)
=> reqCode == RequestFlushTargetState
Expand Down Expand Up @@ -101,7 +101,7 @@ private static int HandleComputeArgGCRefMap(Target target, uint inSize, byte* in
{
req.cbFilled = 0;
Unsafe.WriteUnaligned(inBuffer, req);
return HResultErrorInsufficientBuffer;
return ERROR_INSUFFICIENT_BUFFER;
}

byte* dest = (byte*)(nuint)req.BlobBuffer;
Expand Down
86 changes: 85 additions & 1 deletion src/native/managed/cdac/tests/UnitTests/TypeHandleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using Microsoft.Diagnostics.DataContractReader;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Legacy;
using Microsoft.Diagnostics.DataContractReader.TestInfrastructure;
Expand All @@ -15,7 +16,7 @@

namespace Microsoft.Diagnostics.DataContractReader.Tests;

public class TypeHandleTests
public unsafe class TypeHandleTests
{
private const ulong ModuleAddress = 0x1000;

Expand Down Expand Up @@ -140,6 +141,86 @@ public void GetName_MethodTablesMatchNative(MockTarget.Architecture architecture
Assert.Equal("System.Int32[*]", rankOneArrayType.GetName(target));
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void ClrDataTypeInstance_GetName(MockTarget.Architecture architecture)
{
TargetTypeHandle typeHandle = new(0x2002);
TestRuntimeTypeSystem runtimeTypeSystem = new();
runtimeTypeSystem.TypeDescs.Add(typeHandle);
runtimeTypeSystem.ElementTypes[typeHandle] = CorElementType.I4;
TestPlaceholderTarget target = CreateTarget(architecture, runtimeTypeSystem);
IXCLRDataTypeInstance typeInstance = new ClrDataTypeInstance(target, typeHandle, null);

uint nameLen = 0;
Assert.Equal(HResults.S_OK, typeInstance.GetName(0, 0, &nameLen, null));
Assert.Equal(13u, nameLen);

char[] nameBuffer = new char[nameLen];
fixed (char* name = nameBuffer)
{
Assert.Equal(HResults.S_OK, typeInstance.GetName(0, nameLen, &nameLen, name));
Assert.Equal("System.Int32", new string(name, 0, (int)nameLen - 1));

Assert.Equal(CorDbgHResults.ERROR_INSUFFICIENT_BUFFER, typeInstance.GetName(0, 4, &nameLen, name));
Assert.Equal("Sys", new string(name, 0, 3));
}

Assert.Equal(HResults.E_INVALIDARG, typeInstance.GetName(1, 0, null, null));
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void ClrDataTypeInstance_GetDefinition(MockTarget.Architecture architecture)
{
const uint TypeDefToken = 0x02000001;
TargetPointer lookupMap = new(0x2000);
TargetPointer definitionTypeAddress = new(0x3000);
TargetTypeHandle typeHandle = new(0x4000);
TargetTypeHandle definitionType = new(definitionTypeAddress);
Contracts.ModuleHandle moduleHandle = new(ModuleAddress);

TestRuntimeTypeSystem runtimeTypeSystem = new();
runtimeTypeSystem.TypeModules[typeHandle] = ModuleAddress;
runtimeTypeSystem.TypeModules[definitionType] = ModuleAddress;
runtimeTypeSystem.TypeDefTokens[typeHandle] = TypeDefToken;
runtimeTypeSystem.TypeDefTokens[definitionType] = TypeDefToken;
runtimeTypeSystem.TypeHandles[definitionTypeAddress] = definitionType;

ModuleLookupTables lookupTables = new(
FieldDefToDesc: TargetPointer.Null,
ManifestModuleReferences: TargetPointer.Null,
MemberRefToDesc: TargetPointer.Null,
MethodDefToDesc: TargetPointer.Null,
TypeDefToMethodTable: lookupMap,
TypeRefToMethodTable: TargetPointer.Null,
MethodDefToILCodeVersioningState: TargetPointer.Null,
TableDataOffset: 0);
Mock<ILoader> loader = new();
loader.Setup(l => l.GetModuleHandleFromModulePtr(new TargetPointer(ModuleAddress))).Returns(moduleHandle);
loader.Setup(l => l.GetLookupTables(moduleHandle)).Returns(lookupTables);
TargetNUInt lookupFlags = default;
loader.Setup(l => l.GetModuleLookupMapElement(lookupMap, TypeDefToken, out lookupFlags)).Returns(definitionTypeAddress);

TestPlaceholderTarget target = new TestPlaceholderTarget.Builder(architecture)
.AddMockContract<IRuntimeTypeSystem>(runtimeTypeSystem)
.AddMockContract(loader)
.Build();
IXCLRDataTypeInstance typeInstance = new ClrDataTypeInstance(target, typeHandle, null);
DacComNullableByRef<IXCLRDataTypeDefinition> typeDefinition = new(isNullRef: false);

Assert.Equal(HResults.S_OK, typeInstance.GetDefinition(typeDefinition));
Assert.IsType<ClrDataTypeDefinition>(typeDefinition.Interface);

DacComNullableByRef<IXCLRDataTypeDefinition> nullTypeDefinition = new(isNullRef: true);
Assert.Equal(HResults.E_POINTER, typeInstance.GetDefinition(nullTypeDefinition));

loader.Setup(l => l.GetModuleLookupMapElement(lookupMap, TypeDefToken, out lookupFlags)).Returns(TargetPointer.Null);
DacComNullableByRef<IXCLRDataTypeDefinition> unloadedTypeDefinition = new(isNullRef: false);
Assert.Equal(HResults.S_OK, typeInstance.GetDefinition(unloadedTypeDefinition));
Assert.IsType<ClrDataTypeDefinition>(unloadedTypeDefinition.Interface);
}

private static TestPlaceholderTarget CreateTarget(
MockTarget.Architecture architecture,
TestRuntimeTypeSystem runtimeTypeSystem,
Expand Down Expand Up @@ -201,6 +282,7 @@ private sealed class TestRuntimeTypeSystem : IRuntimeTypeSystem
public Dictionary<ITypeHandle, uint> ArrayRanks { get; } = [];
public Dictionary<ITypeHandle, uint> TypeDefTokens { get; } = [];
public Dictionary<ITypeHandle, TargetPointer> TypeModules { get; } = [];
public Dictionary<TargetPointer, ITypeHandle> TypeHandles { get; } = [];
public Dictionary<ITypeHandle, ITypeHandle[]> Instantiations { get; } = [];
public Dictionary<ITypeHandle, (TargetPointer Module, uint Token, uint Index)> GenericVariables { get; } = [];

Expand All @@ -220,6 +302,8 @@ private sealed class TestRuntimeTypeSystem : IRuntimeTypeSystem

public TargetPointer GetModule(ITypeHandle typeHandle) => TypeModules.GetValueOrDefault(typeHandle);

public ITypeHandle GetTypeHandle(TargetPointer address) => TypeHandles[address];

public ReadOnlySpan<ITypeHandle> GetInstantiation(ITypeHandle typeHandle)
=> Instantiations.TryGetValue(typeHandle, out ITypeHandle[]? instantiation) ? instantiation : [];

Expand Down
Loading