Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
21 changes: 17 additions & 4 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ record struct ThreadData (
ThreadStoreData GetThreadStoreData();
ThreadStoreCounts GetThreadCounts();
ThreadData GetThreadData(TargetPointer threadPointer);
TargetPointer IdToThread(uint id);
```

## Version 1
Expand All @@ -58,6 +59,7 @@ This contract depends on the following descriptors:
| `RuntimeThreadLocals` |
| `Thread` |
| `ThreadStore` |
| `IdDispenser` |

| Global name |
| --- |
Expand All @@ -66,7 +68,7 @@ This contract depends on the following descriptors:
| `FeatureEHFunclets` |
| `FinalizerThread` |
| `GCThread` |

| `ThinLockThreadIdDispenser` |
``` csharp
ThreadStoreData GetThreadStoreData()
{
Expand All @@ -93,11 +95,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 +129,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) */);
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