Skip to content
Closed
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
41 changes: 38 additions & 3 deletions docs/design/datacontracts/Object.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPoin

// Get built-in COM data for the object if available. Returns false, if address does not represent a COM object using built-in COM
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);

// Get the object's tagged memory (if it exists).
TargetPointer TaggedMemory(TargetPointer address);

// Get the tagged memory size (if applicable).
nuint GetTaggedMemorySize();
```

## Version 1
Expand All @@ -26,6 +32,7 @@ Data descriptors used:
| `Array` | `m_NumComponents` | Number of items in the array |
| `InteropSyncBlockInfo` | `RCW` | Pointer to the RCW for the object (if it exists) |
| `InteropSyncBlockInfo` | `CCW` | Pointer to the CCW for the object (if it exists) |
| `InteropSyncBlockInfo` | `TaggedMemory` | Pointer to the tagged memory for the object (if it exists) |
| `Object` | `m_pMethTab` | Method table for the object |
| `String` | `m_FirstChar` | First character of the string - `m_StringLength` can be used to read the full string (encoded in UTF-16) |
| `String` | `m_StringLength` | Length of the string in characters (encoded in UTF-16) |
Expand Down Expand Up @@ -93,17 +100,17 @@ TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPoin
{
// Single-dimensional, zero-based - doesn't have bounds
boundsStart = address + /* Array::m_NumComponents offset */;
lowerBounds = _target.ReadGlobalPointer("ArrayBoundsZero");
lowerBounds = target.ReadGlobalPointer("ArrayBoundsZero");
}

// Sync block is before `this` pointer, so substract the object header size
ulong dataOffset = typeSystemContract.GetBaseSize(typeHandle) - _target.ReadGlobal<uint>("ObjectHeaderSize");
ulong dataOffset = typeSystemContract.GetBaseSize(typeHandle) - target.ReadGlobal<uint>("ObjectHeaderSize");
return address + dataOffset;
}

bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw);
{
uint syncBlockValue = target.Read<uint>(address - _target.ReadGlobal<ushort>("SyncBlockValueToObjectOffset"));
uint syncBlockValue = target.Read<uint>(address - target.ReadGlobal<ushort>("SyncBlockValueToObjectOffset"));

// Check if the sync block value represents a sync block index
if ((syncBlockValue & (uint)(SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex | SyncBlockValue.Bits.IsHashCode)) != (uint)SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex)
Expand All @@ -125,4 +132,32 @@ bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetP
ccw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
return rcw != TargetPointer.Null && ccw != TargetPointer.Null;
}

TargetPointer TaggedMemory(TargetPointer address)
{
uint syncBlockValue = target.Read<uint>(address - target.ReadGlobal<ushort>("SyncBlockValueToObjectOffset"));

// Check if the sync block value represents a sync block index
if ((syncBlockValue & (uint)(SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex | SyncBlockValue.Bits.IsHashCode)) != (uint)SyncBlockValue.Bits.IsHashCodeOrSyncBlockIndex)
return TargetPointer.Null;

// Get the offset into the sync table entries
uint index = syncBlockValue & SyncBlockValue.SyncBlockIndexMask;
ulong offsetInSyncTableEntries = index * /* SyncTableEntry size */;

TargetPointer syncBlock = target.ReadPointer(_syncTableEntries + offsetInSyncTableEntries + /* SyncTableEntry::SyncBlock offset */);
if (syncBlock == TargetPointer.Null)
return TargetPointer.Null;

TargetPointer interopInfo = target.ReadPointer(syncBlock + /* SyncTableEntry::InteropInfo offset */);
if (interopInfo == TargetPointer.Null)
return TargetPointer.Null;

return target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::TaggedMemory offset */);
}

nuint GetTaggedMemorySize()
Comment thread
rcj1 marked this conversation as resolved.
{
return 2 * (nuint)target.PointerSize;
Comment thread
rcj1 marked this conversation as resolved.
}
```
3 changes: 3 additions & 0 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ partial interface IRuntimeTypeSystem : IContract
public ushort GetNumStaticFields(TypeHandle typeHandle);
public ushort GetNumThreadStaticFields(TypeHandle typeHandle);
public TargetPointer GetFieldDescList(TypeHandle typeHandle);
public bool IsTrackedReferenceWithFinalizer(TypeHandle typeHandle);
public virtual ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle);
public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle);

Expand Down Expand Up @@ -459,6 +460,8 @@ The contract additionally depends on these data descriptors
public ushort GetNumThreadStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumThreadStaticFields;

public TargetPointer GetFieldDescList(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).FieldDescList;
public bool IsTrackedReferenceWithFinalizer(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.IsTrackedReferenceWithFinalizer) != 0;


public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle TypeHandle)
{
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ CDAC_TYPE_INDETERMINATE(InteropSyncBlockInfo)
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCW, cdac_data<InteropSyncBlockInfo>::CCW)
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, RCW, cdac_data<InteropSyncBlockInfo>::RCW)
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_OBJCMARSHAL
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, TaggedMemory, cdac_data<InteropSyncBlockInfo>::TaggedMemory)
#endif // FEATURE_OBJCMARSHAL
CDAC_TYPE_END(InteropSyncBlockInfo)

CDAC_TYPE_BEGIN(SyncBlock)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/syncblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,9 @@ struct cdac_data<InteropSyncBlockInfo>
#ifdef FEATURE_COMINTEROP
static constexpr size_t CCW = offsetof(InteropSyncBlockInfo, m_pCCW);
static constexpr size_t RCW = offsetof(InteropSyncBlockInfo, m_pRCW);
#endif
#ifdef FEATURE_OBJCMARSHAL
static constexpr size_t TaggedMemory = offsetof(InteropSyncBlockInfo, m_taggedMemory);
#endif // FEATURE_COMINTEROP
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface IObject : IContract
string GetStringValue(TargetPointer address) => throw new NotImplementedException();
TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds) => throw new NotImplementedException();
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw) => throw new NotImplementedException();
TargetPointer TaggedMemory(TargetPointer address) => throw new NotImplementedException();
nuint GetTaggedMemorySize() => throw new NotImplementedException();
}

public readonly struct Object : IObject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public interface IRuntimeTypeSystem : IContract
ushort GetNumStaticFields(TypeHandle typeHandle) => throw new NotImplementedException();
ushort GetNumThreadStaticFields(TypeHandle typeHandle) => throw new NotImplementedException();
TargetPointer GetFieldDescList(TypeHandle typeHandle) => throw new NotImplementedException();

bool IsTrackedReferenceWithFinalizer(TypeHandle typeHandle) => throw new NotImplementedException();

ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsGenericTypeDefinition(TypeHandle typeHandle) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
private readonly byte _objectToMethodTableUnmask;
private readonly TargetPointer _stringMethodTable;
private readonly TargetPointer _syncTableEntries;
private readonly nuint _taggedMemorySize;

private static class SyncBlockValue
{
Expand All @@ -37,6 +38,7 @@ internal Object_1(Target target, ulong methodTableOffset, byte objectToMethodTab
_stringMethodTable = stringMethodTable;
_objectToMethodTableUnmask = objectToMethodTableUnmask;
_syncTableEntries = syncTableEntries;
_taggedMemorySize = 2 * (nuint)target.PointerSize;
}

public TargetPointer GetMethodTableAddress(TargetPointer address)
Expand Down Expand Up @@ -115,6 +117,17 @@ public bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out
return rcw != TargetPointer.Null || ccw != TargetPointer.Null;
}

public TargetPointer TaggedMemory(TargetPointer address)
{
Data.SyncBlock? syncBlock = GetSyncBlock(address);
return syncBlock?.InteropInfo?.TaggedMemory ?? TargetPointer.Null;
}

public nuint GetTaggedMemorySize()
{
return _taggedMemorySize;
}

private Data.SyncBlock? GetSyncBlock(TargetPointer address)
{
uint syncBlockValue = _target.Read<uint>(address - _target.ReadGlobal<ushort>(Constants.Globals.SyncBlockValueToObjectOffset));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ public uint GetTypeDefToken(TypeHandle typeHandle)
public ushort GetNumStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumStaticFields;
public ushort GetNumThreadStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumThreadStaticFields;
public TargetPointer GetFieldDescList(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).FieldDescList;
public bool IsTrackedReferenceWithFinalizer(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.GetFlag(MethodTableFlags_1.WFLAGS_HIGH.IsTrackedReferenceWithFinalizer) != 0;


public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ public InteropSyncBlockInfo(Target target, TargetPointer address)
CCW = type.Fields.TryGetValue(nameof(CCW), out Target.FieldInfo ccwField)
? target.ReadPointer(address + (ulong)ccwField.Offset)
: TargetPointer.Null;
TaggedMemory = type.Fields.TryGetValue(nameof(TaggedMemory), out Target.FieldInfo taggedMemoryField)
? target.ReadPointer(address + (ulong)taggedMemoryField.Offset)
: TargetPointer.Null;
}

public TargetPointer RCW { get; init; }
public TargetPointer CCW { get; init; }
public TargetPointer TaggedMemory { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ internal enum WFLAGS_HIGH : uint
Category_TruePrimitive = 0x00070000,
Category_Interface = 0x000C0000,
ContainsGCPointers = 0x01000000,
IsTrackedReferenceWithFinalizer = 0x04000000,
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
// otherwise the lower bits are used for WFLAGS_LOW
// otherwise the lower bits are used for WFLAGS_LOW
}

[Flags]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2350,9 +2350,99 @@ int ISOSDacInterface10.GetComWrappersRCWData(ClrDataAddress rcw, ClrDataAddress*

#region ISOSDacInterface11
int ISOSDacInterface11.IsTrackedType(ClrDataAddress objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory)
=> _legacyImpl11 is not null ? _legacyImpl11.IsTrackedType(objAddr, isTrackedType, hasTaggedMemory) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
if (objAddr == 0 || isTrackedType == null || hasTaggedMemory == null)
hr = HResults.E_INVALIDARG;
else
{
try
{
*isTrackedType = Interop.BOOL.FALSE;
*hasTaggedMemory = Interop.BOOL.FALSE;
Contracts.IObject objectContract = _target.Contracts.Object;
Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
TargetPointer objPtr = objAddr.ToTargetPointer(_target);
TargetPointer mt = objectContract.GetMethodTableAddress(objPtr);
if (mt == TargetPointer.Null)
hr = HResults.E_INVALIDARG;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we end up in this condition, both *isTrackedType and *hasTaggedMemory should be false.

else
{
TypeHandle mtHandle = rtsContract.GetTypeHandle(mt);
if (rtsContract.IsTrackedReferenceWithFinalizer(mtHandle))
*isTrackedType = Interop.BOOL.TRUE;
hr = (*isTrackedType == Interop.BOOL.TRUE) ? HResults.S_OK : HResults.S_FALSE;
TargetPointer taggedMemory = objectContract.TaggedMemory(objPtr);
if (taggedMemory != TargetPointer.Null)
*hasTaggedMemory = Interop.BOOL.TRUE;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
}
#if DEBUG
if (_legacyImpl11 is not null)
{
Interop.BOOL isTrackedTypeLocal;
Interop.BOOL hasTaggedMemoryLocal;
int hrLocal = _legacyImpl11.IsTrackedType(objAddr, &isTrackedTypeLocal, &hasTaggedMemoryLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK || hr == HResults.S_FALSE)
{
Debug.Assert(*isTrackedType == isTrackedTypeLocal);
Debug.Assert(*hasTaggedMemory == hasTaggedMemoryLocal);
}
}
#endif
return hr;
}
int ISOSDacInterface11.GetTaggedMemory(ClrDataAddress objAddr, ClrDataAddress* taggedMemory, nuint* taggedMemorySizeInBytes)
=> _legacyImpl11 is not null ? _legacyImpl11.GetTaggedMemory(objAddr, taggedMemory, taggedMemorySizeInBytes) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
if (objAddr == 0 || taggedMemory == null || taggedMemorySizeInBytes == null)
hr = HResults.E_INVALIDARG;
else
{
*taggedMemory = 0;
*taggedMemorySizeInBytes = 0;
try
{
Contracts.IObject objectContract = _target.Contracts.Object;
TargetPointer objPtr = objAddr.ToTargetPointer(_target);
TargetPointer taggedMemoryPtr = objectContract.TaggedMemory(objPtr);
if (taggedMemoryPtr != TargetPointer.Null)
{
*taggedMemory = taggedMemoryPtr.ToClrDataAddress(_target);
*taggedMemorySizeInBytes = objectContract.GetTaggedMemorySize();
}
else
{
hr = HResults.S_FALSE;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
}
#if DEBUG
if (_legacyImpl11 is not null)
{
ClrDataAddress taggedMemoryLocal;
nuint taggedMemorySizeInBytesLocal;
int hrLocal = _legacyImpl11.GetTaggedMemory(objAddr, &taggedMemoryLocal, &taggedMemorySizeInBytesLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK || hr == HResults.S_FALSE)
{
Debug.Assert(*taggedMemory == taggedMemoryLocal);
Debug.Assert(*taggedMemorySizeInBytes == taggedMemorySizeInBytesLocal);
}
}
#endif
return hr;
}
#endregion ISOSDacInterface11

#region ISOSDacInterface12
Expand Down