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
2 changes: 1 addition & 1 deletion docs/design/datacontracts/GC.md
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ private HandleData CreateHandleData(TargetPointer handleAddress, byte uBlock, ui
{
IObject obj = target.Contracts.Object;
TargetPointer handle = target.ReadPointer(handleAddress);
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw);
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw, out _);
if (ccw != TargetPointer.Null)
{
IBuiltInCOM builtInCOM = target.Contracts.BuiltInCOM;
Expand Down
17 changes: 11 additions & 6 deletions docs/design/datacontracts/Object.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ string GetStringValue(TargetPointer address);
// Get the pointer to the data corresponding to a managed array object. Error if address does not represent a array.
TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds);

// 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 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, out TargetPointer ccf);
```

## Version 1
Expand All @@ -26,6 +26,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` | `CCF` | Pointer to the COM class factory 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 @@ -105,7 +106,7 @@ TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPoin
return address + dataOffset;
}

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

Expand All @@ -125,8 +126,12 @@ bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetP
if (interopInfo == TargetPointer.Null)
return false;

rcw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::RCW offset */);
ccw = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
return rcw != TargetPointer.Null && ccw != TargetPointer.Null;
TargetPointer rcw1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::RCW offset */);
rcw = rcw1 & ~1ul;
TargetPointer ccw1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCW offset */);
ccw = (ccw1 == 1) ? TargetPointer.Null : ccw1;
TargetPointer ccf1 = target.ReadPointer(interopInfo + /* InteropSyncBlockInfo::CCF offset */);
ccf = (ccf1 == 1) ? TargetPointer.Null : ccf1;
return rcw != TargetPointer.Null || ccw != TargetPointer.Null || ccf != TargetPointer.Null;
}
```
144 changes: 144 additions & 0 deletions docs/design/datacontracts/SyncBlock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Contract SyncBlock

This contract is for reading sync block table entries and lock state.

## APIs of contract

``` csharp
TargetPointer GetSyncBlock(uint index);
TargetPointer GetSyncBlockObject(uint index);
bool IsSyncBlockFree(uint index);
uint GetSyncBlockCount();
bool TryGetLockInfo(TargetPointer syncBlock, out uint owningThreadId, out uint recursion);
uint GetAdditionalThreadCount(TargetPointer syncBlock);
```

## Version 1

Data descriptors used:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `SyncTableEntry` | `SyncBlock` | Pointer to the sync block for a sync table entry |
| `SyncTableEntry` | `Object` | Pointer to the object associated with a sync table entry |
| `SyncBlockCache` | `FreeSyncTableIndex` | One past the highest sync table entry index allocated |
| `SyncBlock` | `Lock` | Optional pointer to a `System.Threading.Lock` object payload |
| `SyncBlock` | `ThinLock` | Thin-lock state bits |
| `SyncBlock` | `LinkNext` | Head pointer for additional waiting threads list |
| `SLink` | `Next` | Next link for the additional waiting threads list |

Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| `SyncTableEntries` | TargetPointer | Pointer to the sync table entries array |
| `SyncBlockCache` | TargetPointer | Pointer to the runtime sync block cache |
| `SyncBlockMaskLockThreadId` | uint32 | Mask for extracting thread id from `SyncBlock.ThinLock` |
| `SyncBlockMaskLockRecursionLevel` | uint32 | Mask for extracting recursion level from `SyncBlock.ThinLock` |
| `SyncBlockRecursionLevelShift` | uint32 | Shift value for `SyncBlock.ThinLock` recursion level |

### Contract Constants:
| Name | Type | Purpose | Value |
| --- | --- | --- | --- |
| `LockStateName` | string | Field name in `System.Threading.Lock` storing monitor-held state bits. | `_state` |
| `LockOwningThreadIdName` | string | Field name in `System.Threading.Lock` storing owning thread id. | `_owningThreadId` |
| `LockRecursionCountName` | string | Field name in `System.Threading.Lock` storing monitor recursion count. | `_recursionCount` |
| `LockName` | string | Type name used to resolve `System.Threading.Lock`. | `Lock` |
| `LockNamespace` | string | Namespace used to resolve `System.Threading.Lock`. | `System.Threading` |

Contracts used:
| Contract Name |
| --- |
| `Loader` |
| `RuntimeTypeSystem` |
| `EcmaMetadata` |

``` csharp
TargetPointer GetSyncBlock(uint index)
{
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
return target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::SyncBlock offset */);
}

TargetPointer GetSyncBlockObject(uint index)
{
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
return target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::Object offset */);
}

bool IsSyncBlockFree(uint index)
{
TargetPointer syncTableEntries = target.ReadGlobalPointer("SyncTableEntries");
ulong offsetInSyncTable = index * /* SyncTableEntry size */;
TargetPointer obj = target.ReadPointer(syncTableEntries + offsetInSyncTable + /* SyncTableEntry::Object offset */);
return (obj.Value & 1) != 0;
}

uint GetSyncBlockCount()
{
TargetPointer syncBlockCache = target.ReadPointer(target.ReadGlobalPointer("SyncBlockCache"));
uint freeSyncTableIndex = target.Read<uint>(syncBlockCache + /* SyncBlockCache::FreeSyncTableIndex offset */);
return freeSyncTableIndex - 1;
}

bool TryGetLockInfo(TargetPointer syncBlock, out uint owningThreadId, out uint recursion)
{
owningThreadId = 0;
recursion = 0;

TargetPointer lockObject = target.ReadPointer(syncBlock + /* SyncBlock::Lock offset */);

if (lockObject != TargetPointer.Null)
{
// Resolve System.Threading.Lock in System.Private.CoreLib by name using RuntimeTypeSystem contract, LockName and LockNamespace.
uint state = ReadUintField(/* Lock type */, "LockStateName", /* RuntimeTypeSystem contract */, /* MetadataReader for SPC */, lockObject);
bool monitorHeld = (state & 1) != 0;
if (monitorHeld)
{
owningThreadId = ReadUintField(/* Lock type */, "LockOwningThreadIdName", /* contracts */, lockObject);
recursion = ReadUintField(/* Lock type */, "LockRecursionCountName", /* contracts */, lockObject);
}

return monitorHeld;
}

uint thinLock = target.Read<uint>(syncBlock + /* SyncBlock::ThinLock offset */);
if (thinLock != 0)
{
owningThreadId = thinLock & target.ReadGlobal<uint>("SyncBlockMaskLockThreadId");
bool monitorHeld = owningThreadId != 0;
if (monitorHeld)
{
recursion = (thinLock & target.ReadGlobal<uint>("SyncBlockMaskLockRecursionLevel"))
>> (int)target.ReadGlobal<uint>("SyncBlockRecursionLevelShift");
}

return monitorHeld;
}

return false;
}

private uint ReadUintField(TypeHandle enclosingType, string fieldName, IRuntimeTypeSystem rts, MetadataReader mdReader, TargetPointer dataAddr)
{
TargetPointer field = rts.GetFieldDescByName(enclosingType, fieldName);
uint token = rts.GetFieldDescMemberDef(field);
FieldDefinitionHandle fieldHandle = (FieldDefinitionHandle)MetadataTokens.Handle((int)token);
FieldDefinition fieldDef = mdReader.GetFieldDefinition(fieldHandle);
uint offset = rts.GetFieldDescOffset(field, fieldDef);
return _target.Read<uint>(dataAddr + offset);
}

uint GetAdditionalThreadCount(TargetPointer syncBlock)
{
uint threadCount = 0;
TargetPointer next = target.ReadPointer(syncBlock + /* SyncBlock::LinkNext offset */);
while (next != TargetPointer.Null && threadCount < 1000)
{
threadCount++;
next = target.ReadPointer(next + /* SLink::Next offset */);
}

return threadCount;
}
```
22 changes: 21 additions & 1 deletion src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ CDAC_TYPE_END(GCHandle)
// Object

CDAC_TYPE_BEGIN(Object)
CDAC_TYPE_INDETERMINATE(Object)
CDAC_TYPE_SIZE(sizeof(Object))
CDAC_TYPE_FIELD(Object, /*pointer*/, m_pMethTab, cdac_data<Object>::m_pMethTab)
CDAC_TYPE_END(Object)

Expand All @@ -153,14 +153,23 @@ CDAC_TYPE_INDETERMINATE(InteropSyncBlockInfo)
#ifdef FEATURE_COMINTEROP
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCW, cdac_data<InteropSyncBlockInfo>::CCW)
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, RCW, cdac_data<InteropSyncBlockInfo>::RCW)
CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCF, cdac_data<InteropSyncBlockInfo>::CCF)
#endif // FEATURE_COMINTEROP
CDAC_TYPE_END(InteropSyncBlockInfo)

CDAC_TYPE_BEGIN(SyncBlock)
CDAC_TYPE_INDETERMINATE(SyncBlock)
CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, InteropInfo, cdac_data<SyncBlock>::InteropInfo)
CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, Lock, cdac_data<SyncBlock>::Lock)
CDAC_TYPE_FIELD(SyncBlock, /*uint32*/, ThinLock, cdac_data<SyncBlock>::ThinLock)
CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, LinkNext, cdac_data<SyncBlock>::LinkNext)
CDAC_TYPE_END(SyncBlock)

CDAC_TYPE_BEGIN(SLink)
CDAC_TYPE_INDETERMINATE(SLink)
CDAC_TYPE_FIELD(SLink, /*pointer*/, Next, offsetof(SLink, m_pNext))
CDAC_TYPE_END(SLink)

#ifdef FEATURE_COMWRAPPERS
CDAC_TYPE_BEGIN(NativeObjectWrapperObject)
CDAC_TYPE_INDETERMINATE(NativeObjectWrapperObject)
Expand All @@ -181,6 +190,7 @@ CDAC_TYPE_END(ManagedObjectWrapperLayout)
CDAC_TYPE_BEGIN(SyncTableEntry)
CDAC_TYPE_SIZE(sizeof(SyncTableEntry))
CDAC_TYPE_FIELD(SyncTableEntry, /*pointer*/, SyncBlock, offsetof(SyncTableEntry, m_SyncBlock))
CDAC_TYPE_FIELD(SyncTableEntry, /*pointer*/, Object, offsetof(SyncTableEntry, m_Object))
CDAC_TYPE_END(SyncTableEntry)

// Loader
Expand Down Expand Up @@ -1061,6 +1071,11 @@ CDAC_TYPE_SIZE(g_numKnownQueryInterfaceImplementations * sizeof(PCODE))
CDAC_TYPE_END(ComWrappersVtablePtrs)
#endif

CDAC_TYPE_BEGIN(SyncBlockCache)
CDAC_TYPE_INDETERMINATE(SyncBlockCache)
CDAC_TYPE_FIELD(SyncBlockCache, /*uint32*/, FreeSyncTableIndex, cdac_data<SyncBlockCache>::FreeSyncTableIndex)
CDAC_TYPE_END(SyncBlockCache)

CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
Expand Down Expand Up @@ -1210,6 +1225,10 @@ CDAC_GLOBAL(StressLogEnabled, uint8, 0)
CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data<ExecutionManager>::CodeRangeMapAddress)
CDAC_GLOBAL_POINTER(PlatformMetadata, &::g_cdacPlatformMetadata)
CDAC_GLOBAL_POINTER(ProfilerControlBlock, &::g_profControlBlock)
CDAC_GLOBAL_POINTER(SyncBlockCache, &SyncBlockCache::s_pSyncBlockCache)
CDAC_GLOBAL(SyncBlockMaskLockThreadId, uint32, SBLK_MASK_LOCK_THREADID)
CDAC_GLOBAL(SyncBlockMaskLockRecursionLevel, uint32, SBLK_MASK_LOCK_RECLEVEL)
CDAC_GLOBAL(SyncBlockRecursionLevelShift, uint32, SBLK_RECLEVEL_SHIFT)

CDAC_GLOBAL_POINTER(GCLowestAddress, &g_lowest_address)
CDAC_GLOBAL_POINTER(GCHighestAddress, &g_highest_address)
Expand Down Expand Up @@ -1247,6 +1266,7 @@ CDAC_GLOBAL_CONTRACT(RuntimeInfo, 1)
CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, 1)
CDAC_GLOBAL_CONTRACT(SHash, 1)
CDAC_GLOBAL_CONTRACT(SignatureDecoder, 1)
CDAC_GLOBAL_CONTRACT(SyncBlock, 1)
CDAC_GLOBAL_CONTRACT(StackWalk, 1)
CDAC_GLOBAL_CONTRACT(StressLog, 2)
CDAC_GLOBAL_CONTRACT(Thread, 1)
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/vm/syncblk.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ 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);
static constexpr size_t CCF = offsetof(InteropSyncBlockInfo, m_pCCF);
#endif // FEATURE_COMINTEROP
};

Expand Down Expand Up @@ -601,6 +602,10 @@ template<>
struct cdac_data<SyncBlock>
{
static constexpr size_t InteropInfo = offsetof(SyncBlock, m_pInteropInfo);
static constexpr size_t Lock = offsetof(SyncBlock, m_Lock);
static constexpr size_t ThinLock = offsetof(SyncBlock, m_thinLock);
static constexpr size_t LinkNext = offsetof(SyncBlock, m_Link) + offsetof(SLink, m_pNext);

};

class SyncTableEntry
Expand Down Expand Up @@ -759,6 +764,13 @@ class SyncBlockCache
#ifdef VERIFY_HEAP
void VerifySyncTableEntry();
#endif
friend struct ::cdac_data<SyncBlockCache>;
};

template<>
struct cdac_data<SyncBlockCache>
{
static constexpr size_t FreeSyncTableIndex = offsetof(SyncBlockCache, m_FreeSyncTableIndex);
};

// See code:#SyncBlockOverView for more
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ public sealed class Lock

private static long s_contentionCount;

private int _owningThreadId;
private int _owningThreadId; // cDAC depends on exact name of this field

private uint _state; // see State for layout
private uint _recursionCount;
private uint _state; // see State for layout. cDAC depends on exact name of this field
private uint _recursionCount; // cDAC depends on exact name of this field

// This field serves a few purposes currently:
// - When positive, it indicates the number of spin-wait iterations that most threads would do upon contention
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ public abstract class ContractRegistry
/// </summary>
public virtual ISignatureDecoder SignatureDecoder => GetContract<ISignatureDecoder>();
/// <summary>
/// Gets an instance of the SyncBlock contract for the target.
/// </summary>
public virtual ISyncBlock SyncBlock => GetContract<ISyncBlock>();
/// <summary>
/// Gets an instance of the BuiltInCOM contract for the target.
/// </summary>
public virtual IBuiltInCOM BuiltInCOM => GetContract<IBuiltInCOM>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface IObject : IContract
TargetPointer GetMethodTableAddress(TargetPointer address) => throw new NotImplementedException();
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();
bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf) => throw new NotImplementedException();
}

public readonly struct Object : IObject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ public interface IRuntimeTypeSystem : IContract
bool IsFieldDescStatic(TargetPointer fieldDescPointer) => throw new NotImplementedException();
CorElementType GetFieldDescType(TargetPointer fieldDescPointer) => throw new NotImplementedException();
uint GetFieldDescOffset(TargetPointer fieldDescPointer, FieldDefinition fieldDef) => throw new NotImplementedException();
TargetPointer GetFieldDescByName(TypeHandle typeHandle, string fieldName) => throw new NotImplementedException();
#endregion FieldDesc inspection APIs
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;
public interface ISyncBlock : IContract
{
static string IContract.Name { get; } = nameof(SyncBlock);
TargetPointer GetSyncBlock(uint index) => throw new NotImplementedException();
TargetPointer GetSyncBlockObject(uint index) => throw new NotImplementedException();
bool IsSyncBlockFree(uint index) => throw new NotImplementedException();
uint GetSyncBlockCount() => throw new NotImplementedException();
bool TryGetLockInfo(TargetPointer syncBlock, out uint owningThreadId, out uint recursion) => throw new NotImplementedException();
uint GetAdditionalThreadCount(TargetPointer syncBlock) => throw new NotImplementedException();
}

public readonly struct SyncBlock : ISyncBlock
{
// Everything throws NotImplementedException
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ public enum DataType
ThisPtrRetBufPrecodeData,
Array,
SyncBlock,
SLink,
SyncTableEntry,
InteropSyncBlockInfo,
SyncBlockCache,
InstantiatedMethodDesc,
DynamicMethodDesc,
StoredSigMethodDesc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public static class Globals
public const string SyncBlockValueToObjectOffset = nameof(SyncBlockValueToObjectOffset);

public const string SyncTableEntries = nameof(SyncTableEntries);
public const string SyncBlockCache = nameof(SyncBlockCache);
public const string SyncBlockMaskLockThreadId = nameof(SyncBlockMaskLockThreadId);
public const string SyncBlockMaskLockRecursionLevel = nameof(SyncBlockMaskLockRecursionLevel);
public const string SyncBlockRecursionLevelShift = nameof(SyncBlockRecursionLevelShift);

public const string ArrayBoundsZero = nameof(ArrayBoundsZero);
public const string CoreLib = nameof(CoreLib);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ private HandleData CreateHandleData(TargetPointer handleAddress, byte uBlock, ui
{
IObject obj = _target.Contracts.Object;
TargetPointer handle = _target.ReadPointer(handleAddress);
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw);
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw, out _);
if (ccw != TargetPointer.Null)
{
IBuiltInCOM builtInCOM = _target.Contracts.BuiltInCOM;
Expand Down
Loading
Loading