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
53 changes: 53 additions & 0 deletions docs/design/datacontracts/BuiltInCOM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Contract BuiltInCOM

This contract is for getting information related to built-in COM.

## APIs of contract

``` csharp
public ulong GetRefCount(TargetPointer ccw);
// Check whether the COM wrappers handle is weak.
public bool IsHandleWeak(TargetPointer ccw);
```

## Version 1

Data descriptors used:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `ComCallWrapper` | `SimpleWrapper` | Address of the associated `SimpleComCallWrapper` |
| `SimpleComCallWrapper` | `RefCount` | The wrapper refcount value |
| `SimpleComCallWrapper` | `Flags` | Bit flags for wrapper properties |

Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| `ComRefcountMask` | `long` | Mask applied to `SimpleComCallWrapper.RefCount` to produce the visible refcount |

Contracts used:
| Contract Name |
| --- |
`None`

``` csharp

private enum Flags
{
IsHandleWeak = 0x4,
}

public ulong GetRefCount(TargetPointer address)
{
var ccw = _target.ReadPointer(address + /* ComCallWrapper::SimpleWrapper offset */);
ulong refCount = _target.Read<ulong>(ccw + /* SimpleComCallWrapper::RefCount offset */);
long refCountMask = _target.ReadGlobal<long>("ComRefcountMask");
return refCount & (ulong)refCountMask;
}

public bool IsHandleWeak(TargetPointer address)
{
var ccw = _target.ReadPointer(address + /* ComCallWrapper::SimpleWrapper offset */);
uint flags = _target.Read<uint>(ccw + /* SimpleComCallWrapper::Flags offset */);
return (flags & (uint)Flags.IsHandleWeak) != 0;
}
```
1 change: 1 addition & 0 deletions docs/design/datacontracts/ComWrappers.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Contracts used:


``` csharp

public TargetPointer GetComWrappersIdentity(TargetPointer address)
{
return _target.ReadPointer(address + /* NativeObjectWrapperObject::ExternalComObject offset */);
Expand Down
181 changes: 178 additions & 3 deletions docs/design/datacontracts/GC.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ public readonly struct GCOomData
GCOomData GetOomData();
GCOomData GetOomData(TargetPointer heapAddress);

// Gets all GC handles of specified types
List<HandleData> GetHandles(HandleType[] types);
// Gets the supported handle types
HandleType[] GetSupportedHandleTypes();
// Converts integer types into HandleType enum
HandleType[] GetHandleTypes(uint[] types);
// Gets the global allocation context pointer and limit
void GetGlobalAllocationContext(out TargetPointer allocPtr, out TargetPointer allocLimit);
```
Expand Down Expand Up @@ -160,6 +166,15 @@ Data descriptors used:
| `OomHistory` | LohP | GC | Large object heap flag indicating if OOM was related to LOH |
| `GCAllocContext` | Pointer | VM | Current GCAllocContext pointer |
| `GCAllocContext` | Limit | VM | Pointer to the GCAllocContext limit |
| `HandleTableMap` | BucketsPtr | GC | Pointer to the bucket pointer array |
| `HandleTableMap` | Next | GC | Pointer to the next handle table map in the linked list |
| `HandleTableBucket` | Table | GC | Pointer to per-heap `HandleTable*` array |
| `HandleTable` | SegmentList | GC | Head of linked list of handle table segments |
| `TableSegment` | NextSegment | GC | Pointer to the next segment |
| `TableSegment` | RgTail | GC | Tail block index per handle type |
| `TableSegment` | RgAllocation | GC | Circular block-list links per block |
| `TableSegment` | RgValue | GC | Start of handle value storage |
| `TableSegment` | RgUserData | GC | Auxiliary per-block metadata (e.g. secondary handle blocks) |
| `GCAllocContext` | AllocBytes | VM | Number of bytes allocated on SOH by this context |
| `GCAllocContext` | AllocBytesLoh | VM | Number of bytes allocated not on SOH by this context |
| `EEAllocContext` | GCAllocationContext | VM | The `GCAllocContext` struct within an `EEAllocContext` |
Expand Down Expand Up @@ -198,16 +213,28 @@ Global variables used:
| `GCHeapExpandMechanisms` | TargetPointer | GC | Data array stored per heap (in workstation builds) |
| `GCHeapInterestingMechanismBits` | TargetPointer | GC | Data array stored per heap (in workstation builds) |
| `CurrentGCState` | uint | GC | `c_gc_state` enum value. Only available when `GCIdentifiers` contains `background`. |
| `DynamicAdaptationMode | int | GC | GC heap dynamic adaptation mode. Only available when `GCIdentifiers` contains `dynamic_heap`. |
| `DynamicAdaptationMode` | int | GC | GC heap dynamic adaptation mode. Only available when `GCIdentifiers` contains `dynamic_heap`. |
| `GCLowestAddress` | TargetPointer | VM | Lowest GC address as recorded by the VM/GC interface |
| `GCHighestAddress` | TargetPointer | VM | Highest GC address as recorded by the VM/GC interface |
| `HandleTableMap` | TargetPointer | GC | Pointer to the head of the handle table map linked list |
| `InitialHandleTableArraySize` | uint | GC | Number of bucket entries in each `HandleTableMap` |
| `HandleBlocksPerSegment` | uint | GC | Number of blocks in each `TableSegment` |
| `HandleMaxInternalTypes` | uint | GC | Number of handle types (length of `TableSegment.RgTail`) |
| `HandlesPerBlock` | uint | GC | Number of handles in each handle block |
| `BlockInvalid` | byte | GC | Sentinel value indicating an invalid handle block index |
| `DebugDestroyedHandleValue` | TargetPointer | GC | Sentinel handle value used for destroyed handles |
| `FeatureCOMInterop` | byte | VM | Non-zero when COM interop support is enabled |
| `FeatureComWrappers` | byte | VM | Non-zero when `ComWrappers` support is enabled |
| `FeatureObjCMarshal` | byte | VM | Non-zero when Objective-C marshal support is enabled |
| `FeatureJavaMarshal` | byte | VM | Non-zero when Java marshal support is enabled |
| `GlobalAllocContext` | TargetPointer | VM | Pointer to the global `EEAllocContext` |
| `TotalCpuCount` | uint | GC | Number of available processors |

Contracts used:
| Contract Name |
| --- |
| _(none)_ |

| BuiltInCOM |
| Object |

Constants used:
| Name | Type | Purpose | Value |
Expand Down Expand Up @@ -556,6 +583,154 @@ private List<TargetNUInt> ReadGCHeapDataArray(TargetPointer arrayStart, uint len
}
```

GetHandles
```csharp
public enum HandleType
{
WeakShort = 0,
WeakLong = 1,
Strong = 2,
Pinned = 3,
RefCounted = 5,
Dependent = 6,
WeakInteriorPointer = 10,
CrossReference = 11,
}

List<HandleData> IGC.GetHandles(HandleType[] types)
{
List<HandleData> handles = new();
TargetPointer handleTableMap = target.ReadGlobalPointer("HandleTableMap");
string[] gcIdentifiers = GetGCIdentifiers();
uint tableCount = 0;
if (gcType.Contains("workstation"))
tableCount = 1;
else
tableCount = target.Read<uint>(target.ReadGlobalPointer("TotalCpuCount"));
// for each handleTableMap in the linked list
while (handleTableMap != TargetPointer.Null)
{
TargetPointer bucketsPtr = target.ReadPointer(handleTableMap + /* HandleTableMap::BucketsPtr offset */);
foreach (/* read global variable "InitialHandleTableArraySize" bucketPtrs starting at bucketsPtr */)
{
if (bucketPtr == TargetPointer.Null)
continue;

for (int j = 0; j < tableCount; j++)
{
// double dereference to iterate handle tables per array element per GC heap - native equivalent = map->pBuckets[i]->pTable[j]
TargetPointer table = target.ReadPointer(bucketPtr + /* HandleTableBucket::Table offset */);
TargetPointer handleTablePtr = target.ReadPointer(table + (ulong)(j * target.PointerSize));
if (handleTablePtr == TargetPointer.Null)
continue;

foreach (HandleType type in types)
{
// initialize segmentPtr and iterate through the linked list of segments.
TargetPointer segmentPtr = target.ReadPointer(handleTablePtr + /* HandleTable::SegmentList offset */);
if (segmentPtr == TargetPointer.Null)
continue;
do
{
GetHandlesForSegment(segmentPtr, type, handles);
segmentPtr = target.ReadPointer(segmentPtr + /* TableSegment::NextSegment offset */);
} while (segmentPtr != TargetPointer.Null);
}
}
}
handleTableMap = target.ReadPointer(handleTableMap + /* HandleTableMap::Next offset */);
}
return handles;
}

HandleType[] IGC.GetSupportedHandleTypes()
{
// currently supported types: WeakShort, WeakLong, Strong, Pinned, Dependent, WeakInteriorPointer, RefCounted (conditional on at least one of global variables "FeatureCOMInterop", "FeatureComWrappers", and "FeatureObjCMarshal"), and CrossReference (conditional on global variable "FeatureJavaMarshal")
}

HandleType[] GetHandleTypes(uint[] types) => // map raw uint into HandleType enum

private void GetHandlesForSegment(TargetPointer segmentPtr, HandleType type, List<HandleData> handles)
{
// GC handles are stored in circular linked lists per segment and handle type.
// RgTail = array of bytes that is global variable "HandleMaxInternalTypes" long.
// Contains tail block indices for each GC handle type.
// RgAllocation = byte array of block indices that are linked together to find all blocks for a given type. It is global variable "HandleBlocksPerSegment" long
// RgUserData = byte array of block indices for extra handle info such as dependent handles. It is also "HandleBlocksPerSegment" long.
// For example, target.Read<byte>(segmentPtr + TableSegment::RgTail offset + x); => RgTail[x];
Debug.Assert(GetInternalHandleType(type) < target.ReadGlobal<uint>("HandleMaxInternalTypes"));
byte uBlock = target.Read<byte>(segmentPtr + /* TableSegment::RgTail offset */ + GetInternalHandleType(type));
if (uBlock == target.ReadGlobal<byte>("BlockInvalid"))
return;
uBlock = target.Read<byte>(segmentPtr + /* TableSegment::RgAllocation offset */ + uBlock);
byte uHead = uBlock;
do
{
GetHandlesForBlock(segmentPtr, uBlock, type, handles);
// update uBlock
uBlock = target.Read<byte>(segmentPtr + /* TableSegment::RgAllocation offset */ + uBlock);
} while (uBlock != uHead);
}

private void GetHandlesForBlock(TargetPointer segmentPtr, byte uBlock, HandleType type, List<HandleData> handles)
{
for (uint k = 0; k < target.ReadGlobal<byte>("HandlesPerBlock"); k++)
{
uint offset = uBlock * target.ReadGlobal<byte>("HandlesPerBlock") + k;
TargetPointer handleAddress = segmentPtr + /* TableSegment::RgValue offset */ + offset * (uint)_target.PointerSize;
TargetPointer handle = _target.ReadPointer(handleAddress);
if (handle == TargetPointer.Null || handle == target.ReadGlobalPointer("DebugDestroyedHandleValue"))
continue;
handles.Add(CreateHandleData(handleAddress, uBlock, k, segmentPtr, type));
}
}

private static bool IsStrongReference(uint type) => // Strong || Pinned;
private static bool HasSecondary(uint type) => // Dependent || WeakInteriorPointer || CrossReference;
private static bool IsRefCounted(uint type) => // RefCounted;
private static uint GetInternalHandleType(HandleType type) => // convert the HandleType enum to the corresponding runtime-dependent constant uint.

private HandleData CreateHandleData(TargetPointer handleAddress, byte uBlock, uint intraBlockIndex, TargetPointer segmentPtr, HandleType type)
{
HandleData handleData = default;
handleData.Handle = handleAddress;
handleData.Type = GetInternalHandleType(type);
handleData.JupiterRefCount = 0;
handleData.IsPegged = false;
handleData.StrongReference = IsStrongReference(type);
if (HasSecondary(type))
{
byte blockIndex = target.Read<byte>(segmentPtr + /* TableSegment::RgUserData offset */ + uBlock);
if (blockIndex == target.ReadGlobal<byte>("BlockInvalid"))
handleData.Secondary = 0;
else
{
uint offset = blockIndex * target.ReadGlobal<byte>("HandlesPerBlock") + intraBlockIndex;
handleData.Secondary = target.ReadPointer(segmentPtr + /* TableSegment::RgValue offset */ + offset * target.PointerSize);
}
}
else
{
handleData.Secondary = 0;
}

if (target.ReadGlobal<byte>("FeatureCOMInterop") != 0 && IsRefCounted(type))
{
IObject obj = target.Contracts.Object;
TargetPointer handle = target.ReadPointer(handleAddress);
obj.GetBuiltInComData(handle, out _, out TargetPointer ccw);
if (ccw != TargetPointer.Null)
{
IBuiltInCOM builtInCOM = target.Contracts.BuiltInCOM;
handleData.RefCount = (uint)builtInCOM.GetRefCount(ccw);
handleData.StrongReference = handleData.StrongReference || (handleData.RefCount > 0 && !builtInCOM.IsHandleWeak(ccw));
}
}

return handleData;
}
```

GetGlobalAllocationContext
```csharp
void IGC.GetGlobalAllocationContext(out TargetPointer allocPtr, out TargetPointer allocLimit)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/gc/datadescriptor/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "gc.h"
#include "gcscan.h"
#include "gchandletableimpl.h"
#include "handletablepriv.h"
#include "gceventstatus.h"

#ifdef SERVER_GC
Expand Down
38 changes: 38 additions & 0 deletions src/coreclr/gc/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,31 @@ CDAC_TYPE_FIELD(OomHistory, /*nuint*/, AvailablePagefileMb, offsetof(oom_history
CDAC_TYPE_FIELD(OomHistory, /*uint32*/, LohP, offsetof(oom_history, loh_p))
CDAC_TYPE_END(OomHistory)

CDAC_TYPE_BEGIN(HandleTableMap)
CDAC_TYPE_INDETERMINATE(HandleTableMap)
CDAC_TYPE_FIELD(HandleTableMap, /*pointer*/, BucketsPtr, offsetof(HandleTableMap, pBuckets))
CDAC_TYPE_FIELD(HandleTableMap, /*pointer*/, Next, offsetof(HandleTableMap, pNext))
CDAC_TYPE_END(HandleTableMap)

CDAC_TYPE_BEGIN(HandleTableBucket)
CDAC_TYPE_INDETERMINATE(HandleTableBucket)
CDAC_TYPE_FIELD(HandleTableBucket, /*pointer*/, Table, offsetof(HandleTableBucket, pTable))
CDAC_TYPE_END(HandleTableBucket)

CDAC_TYPE_BEGIN(HandleTable)
CDAC_TYPE_INDETERMINATE(HandleTable)
CDAC_TYPE_FIELD(HandleTable, /*pointer*/, SegmentList, offsetof(HandleTable, pSegmentList))
CDAC_TYPE_END(HandleTable)

CDAC_TYPE_BEGIN(TableSegment)
CDAC_TYPE_INDETERMINATE(TableSegment)
CDAC_TYPE_FIELD(TableSegment, /*pointer*/, NextSegment, offsetof(TableSegment, pNextSegment))
CDAC_TYPE_FIELD(TableSegment, /*uint8_t[]*/, RgAllocation, offsetof(TableSegment, rgAllocation))
CDAC_TYPE_FIELD(TableSegment, /*uint8_t[]*/, RgTail, offsetof(TableSegment, rgTail))
CDAC_TYPE_FIELD(TableSegment, /*uint8_t[]*/, RgValue, offsetof(TableSegment, rgValue))
CDAC_TYPE_FIELD(TableSegment, /*uint8_t[]*/, RgUserData, offsetof(TableSegment, rgUserData))
CDAC_TYPE_END(TableSegment)

CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
Expand All @@ -90,6 +115,16 @@ CDAC_GLOBAL(CompactReasonsLength, /*uint32*/, MAX_COMPACT_REASONS_COUNT)
CDAC_GLOBAL(ExpandMechanismsLength, /*uint32*/, MAX_EXPAND_MECHANISMS_COUNT)
CDAC_GLOBAL(InterestingMechanismBitsLength, /*uint32*/, MAX_GC_MECHANISM_BITS_COUNT)
CDAC_GLOBAL(GlobalMechanismsLength, /*uint32*/, MAX_GLOBAL_GC_MECHANISMS_COUNT)
CDAC_GLOBAL(InitialHandleTableArraySize, /*uint32*/, INITIAL_HANDLE_TABLE_ARRAY_SIZE)
#ifdef DEBUG_DestroyedHandleValue
CDAC_GLOBAL(DebugDestroyedHandleValue, /*uintptr_t*/, (uintptr_t)DEBUG_DestroyedHandleValue)
#else
CDAC_GLOBAL(DebugDestroyedHandleValue, /*uintptr_t*/, (uintptr_t)(0))
#endif
CDAC_GLOBAL(HandleBlocksPerSegment, /*uint32*/, HANDLE_BLOCKS_PER_SEGMENT)
CDAC_GLOBAL(HandleMaxInternalTypes, /*uint32*/, HANDLE_MAX_INTERNAL_TYPES)
CDAC_GLOBAL(HandlesPerBlock, /*uint32*/, HANDLE_HANDLES_PER_BLOCK)
CDAC_GLOBAL(BlockInvalid, /*uint8*/, BLOCK_INVALID)


#ifndef SERVER_GC
Expand All @@ -116,6 +151,8 @@ CDAC_GLOBAL_POINTER(GCHeapExpandMechanisms, cdac_data<GC_NAMESPACE::gc_heap>::Ex
CDAC_GLOBAL_POINTER(GCHeapInterestingMechanismBits, cdac_data<GC_NAMESPACE::gc_heap>::InterestingMechanismBits)
#endif // !SERVER_GC

CDAC_GLOBAL_POINTER(HandleTableMap, &g_HandleTableMap)

#ifdef SERVER_GC
#define GC_TYPE server
#else // SERVER_GC
Expand Down Expand Up @@ -151,6 +188,7 @@ CDAC_GLOBAL_POINTER(StructureInvalidCount, &GCScan::m_GcStructuresInvalidCnt)
#ifdef SERVER_GC
CDAC_GLOBAL_POINTER(NumHeaps, &GC_NAMESPACE::gc_heap::n_heaps)
CDAC_GLOBAL_POINTER(Heaps, cdac_data<GC_NAMESPACE::gc_heap>::Heaps)
CDAC_GLOBAL_POINTER(TotalCpuCount, &g_totalCpuCount)
#endif // SERVER_GC

#ifdef BACKGROUND_GC
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@
#endif // TARGET_AMD64 || TARGET_ARM64
#include "introsort.h"

extern uint32_t g_totalCpuCount;

#ifdef SERVER_GC
namespace SVR {
#else // SERVER_GC
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/gc/gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ extern "C" uint32_t g_num_processors;

extern VOLATILE(int32_t) g_fSuspensionPending;

extern uint32_t g_totalCpuCount;

::IGCHandleManager* CreateGCHandleManager();

namespace WKS {
Expand Down
Loading
Loading