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
72 changes: 52 additions & 20 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,48 @@ record struct ThreadData (
ThreadStoreData GetThreadStoreData();
ThreadStoreCounts GetThreadCounts();
ThreadData GetThreadData(TargetPointer threadPointer);
TargetPointer IdToThread(uint id);
```

## Version 1

This contract depends on the following descriptors:

| Data descriptor name |
| --- |
| `GCAllocContext` |
| `RuntimeThreadLocals` |
| `Thread` |
| `ThreadStore` |

| Global name |
| --- |
| `AppDomain` |
| `ThreadStore` |
| `FeatureEHFunclets` |
| `FinalizerThread` |
| `GCThread` |

The contract depends on the following globals

| Global name | Type | Meaning |
| --- | --- |
| `AppDomain` | TargetPointer | A pointer to the address of the one AppDomain
| `ThreadStore` | TargetPointer | A pointer to the address of the ThreadStore
| `FeatureEHFunclets` | TargetPointer | 1 if EH funclets are enabled, 0 otherwise
| `FinalizerThread` | TargetPointer | A pointer to the finalizer thread
| `GCThread` | TargetPointer | A pointer to the GC thread
| `ThinLockThreadIdDispenser` | TargetPointer | Dispenser of thinlock IDs for locking objects

The contract additionally depends on these data descriptors

| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `ExceptionInfo` | `PreviousNestedInfo` | Pointer to previous nested exception info |
| `GCAllocContext` | `Pointer` | GC allocation pointer |
| `GCAllocContext` | `Limit` | Allocation limit pointer |
| `IdDispenser` | `HighestId` | Highest possible small thread ID |
| `IdDispenser` | `IdToThread` | Array mapping small thread IDs to thread pointers |
| `RuntimeThreadLocals` | `AllocContext` | GC allocation context for the thread |
| `Thread` | `Id` | Thread identifier |
| `Thread` | `OSId` | Operating system thread identifier |
| `Thread` | `State` | Thread state flags |
| `Thread` | `PreemptiveGCDisabled` | Flag indicating if preemptive GC is disabled |
| `Thread` | `Frame` | Pointer to current frame |
| `Thread` | `TEB` | Thread Environment Block pointer |
| `Thread` | `LastThrownObject` | Handle to last thrown exception object |
| `Thread` | `LinkNext` | Pointer to get next thread |
| `Thread` | `ExceptionTracker` | Pointer to exception tracking information |
| `Thread` | `RuntimeThreadLocals` | Pointer to some thread-local storage |
| `ThreadStore` | `ThreadCount` | Number of threads |
| `ThreadStore` | `FirstThreadLink` | Pointer to first thread in the linked list |
| `ThreadStore` | `UnstartedCount` | Number of unstarted threads |
| `ThreadStore` | `BackgroundCount` | Number of background threads |
| `ThreadStore` | `PendingCount` | Number of pending threads |
| `ThreadStore` | `DeadCount` | Number of dead threads |
``` csharp
ThreadStoreData GetThreadStoreData()
{
Expand All @@ -93,11 +114,11 @@ DacThreadStoreCounts GetThreadCounts()

ThreadData GetThreadData(TargetPointer address)
{
var runtimeThread = new Thread(Target, threadPointer);
var runtimeThread = new Thread(target, threadPointer);

// Exception tracker is a pointer when EH funclets are enabled
TargetPointer exceptionTrackerAddr = _target.ReadGlobal<byte>("FeatureEHFunclets") != 0
? _target.ReadPointer(address + /* Thread::ExceptionTracker offset */)
TargetPointer exceptionTrackerAddr = target.ReadGlobal<byte>("FeatureEHFunclets") != 0
? target.ReadPointer(address + /* Thread::ExceptionTracker offset */)
: address + /* Thread::ExceptionTracker offset */;
TargetPointer firstNestedException = exceptionTrackerAddr != TargetPointer.Null
? target.ReadPointer(exceptionTrackerAddr + /* ExceptionInfo::PreviousNestedInfo offset*/)
Expand Down Expand Up @@ -127,4 +148,15 @@ ThreadData GetThreadData(TargetPointer address)
NextThread: target.ReadPointer(address + /* Thread::LinkNext offset */) - threadLinkOffset;
);
}

TargetPointer IThread.IdToThread(uint id)
{
TargetPointer idDispenserPointer = target.ReadGlobalPointer(Constants.Globals.ThinlockThreadIdDispenser);
TargetPointer idDispenser = target.ReadPointer(idDispenserPointer);
uint HighestId = target.ReadPointer(idDispenser + /* IdDispenser::HighestId offset */);
TargetPointer threadPtr = TargetPointer.Null;
if (id < HighestId)
threadPtr = target.ReadPointer(idDispenser + /* IdDispenser::IdToThread offset + (index into IdToThread array * size of array elements (== size of target pointer)) */);
return threadPtr;
}
```
7 changes: 7 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals)
CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context))
CDAC_TYPE_END(RuntimeThreadLocals)

CDAC_TYPE_BEGIN(IdDispenser)
CDAC_TYPE_INDETERMINATE(IdDispenser)
CDAC_TYPE_FIELD(IdDispenser, /*pointer*/, IdToThread, cdac_data<IdDispenser>::IdToThread)
CDAC_TYPE_FIELD(IdDispenser, /*uint32*/, HighestId, cdac_data<IdDispenser>::HighestId)
CDAC_TYPE_END(IdDispenser)

CDAC_TYPE_BEGIN(EEAllocContext)
CDAC_TYPE_INDETERMINATE(EEAllocContext)
CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext))
Expand Down Expand Up @@ -992,6 +998,7 @@ CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress)
CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags)
CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo)
CDAC_GLOBAL_POINTER(ThinlockThreadIdDispenser, &::g_pThinLockThreadIdDispenser)
#ifdef TARGET_WINDOWS
CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index)
#endif // TARGET_WINDOWS
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -4390,7 +4390,17 @@ class IdDispenser
_ASSERTE(result == NULL || (dac_cast<size_t>(result) & 0x3) == 0 || ((Thread*)result)->GetThreadId() == id);
return result;
}

friend struct ::cdac_data<IdDispenser>;
};

template<>
struct cdac_data<IdDispenser>
{
static constexpr size_t IdToThread = offsetof(IdDispenser, m_idToThread);
static constexpr size_t HighestId = offsetof(IdDispenser, m_highestId);
};

typedef DPTR(IdDispenser) PTR_IdDispenser;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public interface IThread : IContract
ThreadStoreData GetThreadStoreData() => throw new NotImplementedException();
ThreadStoreCounts GetThreadCounts() => throw new NotImplementedException();
ThreadData GetThreadData(TargetPointer thread) => throw new NotImplementedException();
TargetPointer IdToThread(uint id) => throw new NotImplementedException();
}

public readonly struct Thread : IThread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum DataType
Exception,
ExceptionInfo,
RuntimeThreadLocals,
IdDispenser,
Module,
ModuleLookupMap,
AppDomain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static class Globals
public const string DacNotificationFlags = nameof(DacNotificationFlags);
public const string OffsetOfCurrentThreadInfo = nameof(OffsetOfCurrentThreadInfo);
public const string TlsIndexBase = nameof(TlsIndexBase);
public const string ThinlockThreadIdDispenser = nameof(ThinlockThreadIdDispenser);

public const string StressLogEnabled = nameof(StressLogEnabled);
public const string StressLogHasModuleTable = nameof(StressLogHasModuleTable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
GetThreadFromLink(thread.LinkNext));
}

// happens inside critical section
TargetPointer IThread.IdToThread(uint id)
{
TargetPointer idDispenserPtr = _target.ReadGlobalPointer(Constants.Globals.ThinlockThreadIdDispenser);
TargetPointer idDispenser = _target.ReadPointer(idDispenserPtr);
Data.IdDispenser idDispenserObj = _target.ProcessedData.GetOrAdd<Data.IdDispenser>(idDispenser);
TargetPointer threadPtr = TargetPointer.Null;
if (id < idDispenserObj.HighestId)
threadPtr = _target.ReadPointer(idDispenserObj.IdToThread + (ulong)(id * _target.PointerSize));
return threadPtr;
}

private TargetPointer GetThreadFromLink(TargetPointer threadLink)
{
if (threadLink == TargetPointer.Null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.Data;

internal sealed class IdDispenser : IData<IdDispenser>
{
static IdDispenser IData<IdDispenser>.Create(Target target, TargetPointer address) => new IdDispenser(target, address);
public IdDispenser(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.IdDispenser);
IdToThread = target.ReadPointer(address + (ulong)type.Fields[nameof(IdToThread)].Offset);
HighestId = target.Read<uint>(address + (ulong)type.Fields[nameof(HighestId)].Offset);
}

public TargetPointer IdToThread { get; init; }
public uint HighestId { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -1920,7 +1920,33 @@ int ISOSDacInterface.GetThreadData(ClrDataAddress thread, DacpThreadData* data)
return hr;
}
int ISOSDacInterface.GetThreadFromThinlockID(uint thinLockId, ClrDataAddress* pThread)
=> _legacyImpl is not null ? _legacyImpl.GetThreadFromThinlockID(thinLockId, pThread) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
if (pThread == null)
hr = HResults.E_INVALIDARG;
try
{
TargetPointer threadPtr = _target.Contracts.Thread.IdToThread(thinLockId);
*pThread = threadPtr.ToClrDataAddress(_target);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyImpl is not null)
{
ClrDataAddress pThreadLocal;
int hrLocal = _legacyImpl.GetThreadFromThinlockID(thinLockId, &pThreadLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(*pThread == pThreadLocal);
}
}
#endif
return hr;
}
int ISOSDacInterface.GetThreadLocalModuleData(ClrDataAddress thread, uint index, void* data)
{
// CoreCLR does not use thread local modules anymore
Expand Down
Loading