Skip to content
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
8 changes: 4 additions & 4 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ CDAC_TYPE_SIZE(sizeof(MethodTable))
CDAC_TYPE_FIELD(MethodTable, T_UINT32, MTFlags, cdac_data<MethodTable>::MTFlags)
CDAC_TYPE_FIELD(MethodTable, T_UINT32, BaseSize, cdac_data<MethodTable>::BaseSize)
CDAC_TYPE_FIELD(MethodTable, T_UINT32, MTFlags2, cdac_data<MethodTable>::MTFlags2)
CDAC_TYPE_FIELD(MethodTable, T_NUINT, EEClassOrCanonMT, cdac_data<MethodTable>::EEClassOrCanonMT)
CDAC_TYPE_FIELD(MethodTable, T_POINTER, EEClassOrCanonMT, cdac_data<MethodTable>::EEClassOrCanonMT)
CDAC_TYPE_FIELD(MethodTable, T_POINTER, Module, cdac_data<MethodTable>::Module)
CDAC_TYPE_FIELD(MethodTable, T_POINTER, ParentMethodTable, cdac_data<MethodTable>::ParentMethodTable)
CDAC_TYPE_FIELD(MethodTable, T_UINT16, NumInterfaces, cdac_data<MethodTable>::NumInterfaces)
Expand All @@ -404,8 +404,8 @@ CDAC_TYPE_END(MethodTable)

CDAC_TYPE_BEGIN(DynamicStaticsInfo)
CDAC_TYPE_SIZE(sizeof(DynamicStaticsInfo))
CDAC_TYPE_FIELD(DynamicStaticsInfo, T_UINT32, GCStatics, offsetof(DynamicStaticsInfo, m_pGCStatics))
CDAC_TYPE_FIELD(DynamicStaticsInfo, T_UINT32, NonGCStatics, offsetof(DynamicStaticsInfo, m_pNonGCStatics))
CDAC_TYPE_FIELD(DynamicStaticsInfo, T_POINTER, GCStatics, offsetof(DynamicStaticsInfo, m_pGCStatics))
CDAC_TYPE_FIELD(DynamicStaticsInfo, T_POINTER, NonGCStatics, offsetof(DynamicStaticsInfo, m_pNonGCStatics))
CDAC_TYPE_END(DynamicStaticsInfo)

CDAC_TYPE_BEGIN(MethodTableAuxiliaryData)
Expand Down Expand Up @@ -882,7 +882,7 @@ CDAC_TYPE_BEGIN(NativeCodeVersionNode)
CDAC_TYPE_INDETERMINATE(NativeCodeVersionNode)
CDAC_TYPE_FIELD(NativeCodeVersionNode, T_POINTER, Next, cdac_data<NativeCodeVersionNode>::Next)
CDAC_TYPE_FIELD(NativeCodeVersionNode, T_POINTER, MethodDesc, cdac_data<NativeCodeVersionNode>::MethodDesc)
CDAC_TYPE_FIELD(NativeCodeVersionNode, T_POINTER, NativeCode, cdac_data<NativeCodeVersionNode>::NativeCode)
CDAC_TYPE_FIELD(NativeCodeVersionNode, TYPE(CodePointer), NativeCode, cdac_data<NativeCodeVersionNode>::NativeCode)
CDAC_TYPE_FIELD(NativeCodeVersionNode, T_UINT32, Flags, cdac_data<NativeCodeVersionNode>::Flags)
CDAC_TYPE_FIELD(NativeCodeVersionNode, T_NUINT, ILVersionId, cdac_data<NativeCodeVersionNode>::ILVersionId)
#ifdef HAVE_GCCOVER
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Numerics;
using Microsoft.Diagnostics.DataContractReader.Data;

namespace Microsoft.Diagnostics.DataContractReader;

/// <summary>
/// Extension methods for <see cref="Target"/> that provide typed field reading with optional
/// type validation. When the data descriptor includes type information (debug/checked builds),
/// these methods assert that the declared field type is compatible with the C# read type.
/// </summary>
public static class TargetFieldExtensions
{
/// <summary>
/// Read a primitive integer field from the target with type validation.
/// </summary>
public static T ReadField<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
AssertPrimitiveType<T>(field, fieldName);

return target.Read<T>(address + (ulong)field.Offset);
}

/// <summary>
/// Read an optional primitive integer field from the target with type validation.
/// Returns <paramref name="defaultValue"/> if the field is not present in the descriptor.
/// </summary>
public static T ReadFieldOrDefault<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName, T defaultValue = default)
where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
return defaultValue;

AssertPrimitiveType<T>(field, fieldName);

return target.Read<T>(address + (ulong)field.Offset);
}

/// <summary>
/// Read a pointer field from the target with type validation.
/// </summary>
public static TargetPointer ReadPointerField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
AssertPointerType(field, fieldName);

return target.ReadPointer(address + (ulong)field.Offset);
}

/// <summary>
/// Read an optional pointer field from the target with type validation.
/// Returns <see cref="TargetPointer.Null"/> if the field is not present in the descriptor.
/// </summary>
public static TargetPointer ReadPointerFieldOrNull(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
{
if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
return TargetPointer.Null;

AssertPointerType(field, fieldName);

return target.ReadPointer(address + (ulong)field.Offset);
}

/// <summary>
/// Read a native unsigned integer field from the target with type validation.
/// </summary>
public static TargetNUInt ReadNUIntField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
Debug.Assert(
field.TypeName is null or "" or "nuint",
$"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected nuint");

return target.ReadNUInt(address + (ulong)field.Offset);
}

/// <summary>
/// Read a code pointer field from the target with type validation.
/// </summary>
public static TargetCodePointer ReadCodePointerField(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
Debug.Assert(
field.TypeName is null or "" or "CodePointer",
$"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected CodePointer");

return target.ReadCodePointer(address + (ulong)field.Offset);
}

/// <summary>
/// Read a field that contains an inline Data struct type, with type validation.
/// Returns the data object created by <see cref="Target.IDataCache.GetOrAdd{T}"/>.
/// </summary>
public static T ReadDataField<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
where T : IData<T>
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
Debug.Assert(
field.TypeName is null or "" || field.TypeName == typeof(T).Name,
$"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', reading as {typeof(T).Name}");

return target.ProcessedData.GetOrAdd<T>(address + (ulong)field.Offset);
}

/// <summary>
/// Read a field that contains a pointer to a Data struct type, with type validation.
/// Reads the pointer, then creates the data object via <see cref="Target.IDataCache.GetOrAdd{T}"/>.
/// Returns null if the pointer is null.
/// </summary>
public static T? ReadDataFieldPointer<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
where T : IData<T>
{
Target.FieldInfo field = typeInfo.Fields[fieldName];
AssertPointerType(field, fieldName);

TargetPointer pointer = target.ReadPointer(address + (ulong)field.Offset);
if (pointer == TargetPointer.Null)
return default;

return target.ProcessedData.GetOrAdd<T>(pointer);
}

/// <summary>
/// Read an optional field that contains a pointer to a Data struct type, with type validation.
/// Returns null if the field is not present in the descriptor or the pointer is null.
/// </summary>
public static T? ReadDataFieldPointerOrNull<T>(this Target target, ulong address, Target.TypeInfo typeInfo, string fieldName)
where T : IData<T>
{
if (!typeInfo.Fields.TryGetValue(fieldName, out Target.FieldInfo field))
return default;

AssertPointerType(field, fieldName);

TargetPointer pointer = target.ReadPointer(address + (ulong)field.Offset);
if (pointer == TargetPointer.Null)
return default;

return target.ProcessedData.GetOrAdd<T>(pointer);
}

[Conditional("DEBUG")]
private static void AssertPrimitiveType<T>(Target.FieldInfo field, string fieldName)
where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
Debug.Assert(
field.TypeName is null or "" || IsCompatiblePrimitiveType<T>(field.TypeName),
$"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', reading as {typeof(T).Name}");
}

[Conditional("DEBUG")]
private static void AssertPointerType(Target.FieldInfo field, string fieldName)
{
Debug.Assert(
field.TypeName is null or "" or "pointer",
$"Type mismatch reading field '{fieldName}': declared as '{field.TypeName}', expected pointer");
}

private static bool IsCompatiblePrimitiveType<T>(string typeName)
where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>
{
return typeName switch
{
"uint8" => typeof(T) == typeof(byte),
"int8" => typeof(T) == typeof(sbyte),
"uint16" => typeof(T) == typeof(ushort),
"int16" => typeof(T) == typeof(short),
"uint32" => typeof(T) == typeof(uint),
"int32" => typeof(T) == typeof(int),
"uint64" => typeof(T) == typeof(ulong),
"int64" => typeof(T) == typeof(long),
"bool" => typeof(T) == typeof(byte),
_ => false,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ public SimpleComCallWrapperData GetSimpleComCallWrapperData(TargetPointer ccw)
Data.SimpleComCallWrapper data = _target.ProcessedData.GetOrAdd<Data.SimpleComCallWrapper>(sccw);
return new SimpleComCallWrapperData
{
RefCount = data.RefCount & (ulong)ComRefCount.RefCountMask,
IsNeutered = (data.RefCount & (ulong)ComRefCount.CleanupSentinel) != 0,
RefCount = (uint)(data.RefCount & (long)ComRefCount.RefCountMask),
IsNeutered = (data.RefCount & (long)ComRefCount.CleanupSentinel) != 0,
IsAggregated = (data.Flags & (uint)SimpleComCallWrapperFlags.IsAggregated) != 0,
IsExtendsCOMObject = (data.Flags & (uint)SimpleComCallWrapperFlags.IsExtendsCom) != 0,
IsHandleWeak = (data.Flags & (uint)SimpleComCallWrapperFlags.IsHandleWeak) != 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public AppDomain(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.AppDomain);

RootAssembly = target.ReadPointer(address + (ulong)type.Fields[nameof(RootAssembly)].Offset);
RootAssembly = target.ReadPointerField(address, type, nameof(RootAssembly));
DomainAssemblyList = address + (ulong)type.Fields[nameof(DomainAssemblyList)].Offset;
FriendlyName = target.ReadPointer(address + (ulong)type.Fields[nameof(FriendlyName)].Offset);
FriendlyName = target.ReadPointerField(address, type, nameof(FriendlyName));
}

public TargetPointer RootAssembly { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public Array(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.Array);

NumComponents = target.Read<uint>(address + (ulong)type.Fields[Constants.FieldNames.Array.NumComponents].Offset);
NumComponents = target.ReadField<uint>(address, type, Constants.FieldNames.Array.NumComponents);
DataPointer = address + type.Size!.Value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public ArrayListBase(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBase);

Count = target.Read<uint>(address + (ulong)type.Fields[nameof(Count)].Offset);
Count = target.ReadField<uint>(address, type, nameof(Count));
FirstBlock = address + (ulong)type.Fields[nameof(FirstBlock)].Offset;

TargetPointer next = FirstBlock;
Expand Down Expand Up @@ -56,8 +56,8 @@ public ArrayListBlock(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBlock);

Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset);
Size = target.Read<uint>(address + (ulong)type.Fields[nameof(Size)].Offset);
Next = target.ReadPointerField(address, type, nameof(Next));
Size = target.ReadField<uint>(address, type, nameof(Size));
ArrayStart = address + (ulong)type.Fields[nameof(ArrayStart)].Offset;

for (ulong i = 0; i < Size; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ public Assembly(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.Assembly);

Module = target.ReadPointer(address + (ulong)type.Fields[nameof(Module)].Offset);
IsCollectible = target.Read<byte>(address + (ulong)type.Fields[nameof(IsCollectible)].Offset);
IsDynamic = target.Read<byte>(address + (ulong)type.Fields[nameof(IsDynamic)].Offset) != 0;
Error = target.ReadPointer(address + (ulong)type.Fields[nameof(Error)].Offset);
NotifyFlags = target.Read<uint>(address + (ulong)type.Fields[nameof(NotifyFlags)].Offset);
IsLoaded = target.Read<byte>(address + (ulong)type.Fields[nameof(IsLoaded)].Offset) != 0;
Module = target.ReadPointerField(address, type, nameof(Module));
IsCollectible = target.ReadField<byte>(address, type, nameof(IsCollectible));
IsDynamic = target.ReadField<byte>(address, type, nameof(IsDynamic)) != 0;
Error = target.ReadPointerField(address, type, nameof(Error));
NotifyFlags = target.ReadField<uint>(address, type, nameof(NotifyFlags));
IsLoaded = target.ReadField<byte>(address, type, nameof(IsLoaded)) != 0;
}

public TargetPointer Module { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal sealed class AssemblyBinder : IData<AssemblyBinder>
public AssemblyBinder(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.AssemblyBinder);
AssemblyLoadContext = target.ReadPointer(address + (ulong)type.Fields[nameof(AssemblyLoadContext)].Offset);
AssemblyLoadContext = target.ReadPointerField(address, type, nameof(AssemblyLoadContext));
}
public TargetPointer AssemblyLoadContext { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public AuxiliarySymbolInfo(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.AuxiliarySymbolInfo);

Address = target.ReadCodePointer(address + (ulong)type.Fields[nameof(Address)].Offset);
Name = target.ReadPointer(address + (ulong)type.Fields[nameof(Name)].Offset);
Address = target.ReadCodePointerField(address, type, nameof(Address));
Name = target.ReadPointerField(address, type, nameof(Name));
}

public TargetCodePointer Address { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public CGrowableSymbolStream(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.CGrowableSymbolStream);

Buffer = target.ReadPointer(address + (ulong)type.Fields[nameof(Buffer)].Offset);
Size = target.Read<uint>(address + (ulong)type.Fields[nameof(Size)].Offset);
Buffer = target.ReadPointerField(address, type, nameof(Buffer));
Size = target.ReadField<uint>(address, type, nameof(Size));
}

public TargetPointer Buffer { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ static CardTableInfo IData<CardTableInfo>.Create(Target target, TargetPointer ad
public CardTableInfo(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.CardTableInfo);
Recount = target.Read<uint>(address + (ulong)type.Fields[nameof(Recount)].Offset);
Size = target.ReadNUInt(address + (ulong)type.Fields[nameof(Size)].Offset);
NextCardTable = target.ReadPointer(address + (ulong)type.Fields[nameof(NextCardTable)].Offset);
Recount = target.ReadField<uint>(address, type, nameof(Recount));
Size = target.ReadNUIntField(address, type, nameof(Size));
NextCardTable = target.ReadPointerField(address, type, nameof(NextCardTable));
}

public uint Recount { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ static CodeHeap IData<CodeHeap>.Create(Target target, TargetPointer address)
public CodeHeap(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.CodeHeap);
HeapType = target.Read<byte>(address + (ulong)type.Fields[nameof(HeapType)].Offset);
HeapType = target.ReadField<byte>(address, type, nameof(HeapType));
}

public byte HeapType { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ static CodeHeapListNode IData<CodeHeapListNode>.Create(Target target, TargetPoin
public CodeHeapListNode(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.CodeHeapListNode);
Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset);
StartAddress = target.ReadPointer(address + (ulong)type.Fields[nameof(StartAddress)].Offset);
EndAddress = target.ReadPointer(address + (ulong)type.Fields[nameof(EndAddress)].Offset);
MapBase = target.ReadPointer(address + (ulong)type.Fields[nameof(MapBase)].Offset);
HeaderMap = target.ReadPointer(address + (ulong)type.Fields[nameof(HeaderMap)].Offset);
Heap = target.ReadPointer(address + (ulong)type.Fields[nameof(Heap)].Offset);
Next = target.ReadPointerField(address, type, nameof(Next));
StartAddress = target.ReadPointerField(address, type, nameof(StartAddress));
EndAddress = target.ReadPointerField(address, type, nameof(EndAddress));
MapBase = target.ReadPointerField(address, type, nameof(MapBase));
HeaderMap = target.ReadPointerField(address, type, nameof(HeaderMap));
Heap = target.ReadPointerField(address, type, nameof(Heap));
}

public TargetPointer Next { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ public ComCallWrapper(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ComCallWrapper);

Handle = target.ReadPointer(address + (ulong)type.Fields[nameof(Handle)].Offset);
SimpleWrapper = target.ReadPointer(address + (ulong)type.Fields[nameof(SimpleWrapper)].Offset);
Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset);
Handle = target.ReadPointerField(address, type, nameof(Handle));
SimpleWrapper = target.ReadPointerField(address, type, nameof(SimpleWrapper));
Next = target.ReadPointerField(address, type, nameof(Next));

IPtr = address + (ulong)type.Fields[nameof(IPtr)].Offset;
int numInterfaces = (int)target.ReadGlobal<uint>(Constants.Globals.CCWNumInterfaces);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ public ComMethodTable(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ComMethodTable);

Flags = target.ReadNUInt(address + (ulong)type.Fields[nameof(Flags)].Offset);
MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset);
Flags = target.ReadNUIntField(address, type, nameof(Flags));
MethodTable = target.ReadPointerField(address, type, nameof(MethodTable));
}

public TargetNUInt Flags { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public CoreLibBinder(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.CoreLibBinder);

Classes = target.ReadPointer(address + (ulong)type.Fields[nameof(Classes)].Offset);
Classes = target.ReadPointerField(address, type, nameof(Classes));
}

public TargetPointer Classes { get; init; }
Expand Down
Loading
Loading