Skip to content

Commit 78abe10

Browse files
authored
Adding GetThreadFromThinlockID cDAC API (#118187)
* adding GetThreadFromThinlockID cDAC API * docs
1 parent 1752b96 commit 78abe10

File tree

9 files changed

+131
-21
lines changed

9 files changed

+131
-21
lines changed

docs/design/datacontracts/Thread.md

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,48 @@ record struct ThreadData (
4646
ThreadStoreData GetThreadStoreData();
4747
ThreadStoreCounts GetThreadCounts();
4848
ThreadData GetThreadData(TargetPointer threadPointer);
49+
TargetPointer IdToThread(uint id);
4950
```
5051

5152
## Version 1
5253

53-
This contract depends on the following descriptors:
54-
55-
| Data descriptor name |
56-
| --- |
57-
| `GCAllocContext` |
58-
| `RuntimeThreadLocals` |
59-
| `Thread` |
60-
| `ThreadStore` |
61-
62-
| Global name |
63-
| --- |
64-
| `AppDomain` |
65-
| `ThreadStore` |
66-
| `FeatureEHFunclets` |
67-
| `FinalizerThread` |
68-
| `GCThread` |
69-
54+
The contract depends on the following globals
55+
56+
| Global name | Type | Meaning |
57+
| --- | --- |
58+
| `AppDomain` | TargetPointer | A pointer to the address of the one AppDomain
59+
| `ThreadStore` | TargetPointer | A pointer to the address of the ThreadStore
60+
| `FeatureEHFunclets` | TargetPointer | 1 if EH funclets are enabled, 0 otherwise
61+
| `FinalizerThread` | TargetPointer | A pointer to the finalizer thread
62+
| `GCThread` | TargetPointer | A pointer to the GC thread
63+
| `ThinLockThreadIdDispenser` | TargetPointer | Dispenser of thinlock IDs for locking objects
64+
65+
The contract additionally depends on these data descriptors
66+
67+
| Data Descriptor Name | Field | Meaning |
68+
| --- | --- | --- |
69+
| `ExceptionInfo` | `PreviousNestedInfo` | Pointer to previous nested exception info |
70+
| `GCAllocContext` | `Pointer` | GC allocation pointer |
71+
| `GCAllocContext` | `Limit` | Allocation limit pointer |
72+
| `IdDispenser` | `HighestId` | Highest possible small thread ID |
73+
| `IdDispenser` | `IdToThread` | Array mapping small thread IDs to thread pointers |
74+
| `RuntimeThreadLocals` | `AllocContext` | GC allocation context for the thread |
75+
| `Thread` | `Id` | Thread identifier |
76+
| `Thread` | `OSId` | Operating system thread identifier |
77+
| `Thread` | `State` | Thread state flags |
78+
| `Thread` | `PreemptiveGCDisabled` | Flag indicating if preemptive GC is disabled |
79+
| `Thread` | `Frame` | Pointer to current frame |
80+
| `Thread` | `TEB` | Thread Environment Block pointer |
81+
| `Thread` | `LastThrownObject` | Handle to last thrown exception object |
82+
| `Thread` | `LinkNext` | Pointer to get next thread |
83+
| `Thread` | `ExceptionTracker` | Pointer to exception tracking information |
84+
| `Thread` | `RuntimeThreadLocals` | Pointer to some thread-local storage |
85+
| `ThreadStore` | `ThreadCount` | Number of threads |
86+
| `ThreadStore` | `FirstThreadLink` | Pointer to first thread in the linked list |
87+
| `ThreadStore` | `UnstartedCount` | Number of unstarted threads |
88+
| `ThreadStore` | `BackgroundCount` | Number of background threads |
89+
| `ThreadStore` | `PendingCount` | Number of pending threads |
90+
| `ThreadStore` | `DeadCount` | Number of dead threads |
7091
``` csharp
7192
ThreadStoreData GetThreadStoreData()
7293
{
@@ -93,11 +114,11 @@ DacThreadStoreCounts GetThreadCounts()
93114

94115
ThreadData GetThreadData(TargetPointer address)
95116
{
96-
var runtimeThread = new Thread(Target, threadPointer);
117+
var runtimeThread = new Thread(target, threadPointer);
97118

98119
// Exception tracker is a pointer when EH funclets are enabled
99-
TargetPointer exceptionTrackerAddr = _target.ReadGlobal<byte>("FeatureEHFunclets") != 0
100-
? _target.ReadPointer(address + /* Thread::ExceptionTracker offset */)
120+
TargetPointer exceptionTrackerAddr = target.ReadGlobal<byte>("FeatureEHFunclets") != 0
121+
? target.ReadPointer(address + /* Thread::ExceptionTracker offset */)
101122
: address + /* Thread::ExceptionTracker offset */;
102123
TargetPointer firstNestedException = exceptionTrackerAddr != TargetPointer.Null
103124
? target.ReadPointer(exceptionTrackerAddr + /* ExceptionInfo::PreviousNestedInfo offset*/)
@@ -127,4 +148,15 @@ ThreadData GetThreadData(TargetPointer address)
127148
NextThread: target.ReadPointer(address + /* Thread::LinkNext offset */) - threadLinkOffset;
128149
);
129150
}
151+
152+
TargetPointer IThread.IdToThread(uint id)
153+
{
154+
TargetPointer idDispenserPointer = target.ReadGlobalPointer(Constants.Globals.ThinlockThreadIdDispenser);
155+
TargetPointer idDispenser = target.ReadPointer(idDispenserPointer);
156+
uint HighestId = target.ReadPointer(idDispenser + /* IdDispenser::HighestId offset */);
157+
TargetPointer threadPtr = TargetPointer.Null;
158+
if (id < HighestId)
159+
threadPtr = target.ReadPointer(idDispenser + /* IdDispenser::IdToThread offset + (index into IdToThread array * size of array elements (== size of target pointer)) */);
160+
return threadPtr;
161+
}
130162
```

src/coreclr/debug/runtimeinfo/datadescriptor.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals)
140140
CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context))
141141
CDAC_TYPE_END(RuntimeThreadLocals)
142142

143+
CDAC_TYPE_BEGIN(IdDispenser)
144+
CDAC_TYPE_INDETERMINATE(IdDispenser)
145+
CDAC_TYPE_FIELD(IdDispenser, /*pointer*/, IdToThread, cdac_data<IdDispenser>::IdToThread)
146+
CDAC_TYPE_FIELD(IdDispenser, /*uint32*/, HighestId, cdac_data<IdDispenser>::HighestId)
147+
CDAC_TYPE_END(IdDispenser)
148+
143149
CDAC_TYPE_BEGIN(EEAllocContext)
144150
CDAC_TYPE_INDETERMINATE(EEAllocContext)
145151
CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext))
@@ -992,6 +998,7 @@ CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress)
992998
CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
993999
CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags)
9941000
CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo)
1001+
CDAC_GLOBAL_POINTER(ThinlockThreadIdDispenser, &::g_pThinLockThreadIdDispenser)
9951002
#ifdef TARGET_WINDOWS
9961003
CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index)
9971004
#endif // TARGET_WINDOWS

src/coreclr/vm/threads.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4390,7 +4390,17 @@ class IdDispenser
43904390
_ASSERTE(result == NULL || (dac_cast<size_t>(result) & 0x3) == 0 || ((Thread*)result)->GetThreadId() == id);
43914391
return result;
43924392
}
4393+
4394+
friend struct ::cdac_data<IdDispenser>;
4395+
};
4396+
4397+
template<>
4398+
struct cdac_data<IdDispenser>
4399+
{
4400+
static constexpr size_t IdToThread = offsetof(IdDispenser, m_idToThread);
4401+
static constexpr size_t HighestId = offsetof(IdDispenser, m_highestId);
43934402
};
4403+
43944404
typedef DPTR(IdDispenser) PTR_IdDispenser;
43954405

43964406

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public interface IThread : IContract
4848
ThreadStoreData GetThreadStoreData() => throw new NotImplementedException();
4949
ThreadStoreCounts GetThreadCounts() => throw new NotImplementedException();
5050
ThreadData GetThreadData(TargetPointer thread) => throw new NotImplementedException();
51+
TargetPointer IdToThread(uint id) => throw new NotImplementedException();
5152
}
5253

5354
public readonly struct Thread : IThread

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public enum DataType
2828
Exception,
2929
ExceptionInfo,
3030
RuntimeThreadLocals,
31+
IdDispenser,
3132
Module,
3233
ModuleLookupMap,
3334
AppDomain,

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public static class Globals
3030
public const string DacNotificationFlags = nameof(DacNotificationFlags);
3131
public const string OffsetOfCurrentThreadInfo = nameof(OffsetOfCurrentThreadInfo);
3232
public const string TlsIndexBase = nameof(TlsIndexBase);
33+
public const string ThinlockThreadIdDispenser = nameof(ThinlockThreadIdDispenser);
3334

3435
public const string StressLogEnabled = nameof(StressLogEnabled);
3536
public const string StressLogHasModuleTable = nameof(StressLogHasModuleTable);

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread_1.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
6868
GetThreadFromLink(thread.LinkNext));
6969
}
7070

71+
// happens inside critical section
72+
TargetPointer IThread.IdToThread(uint id)
73+
{
74+
TargetPointer idDispenserPtr = _target.ReadGlobalPointer(Constants.Globals.ThinlockThreadIdDispenser);
75+
TargetPointer idDispenser = _target.ReadPointer(idDispenserPtr);
76+
Data.IdDispenser idDispenserObj = _target.ProcessedData.GetOrAdd<Data.IdDispenser>(idDispenser);
77+
TargetPointer threadPtr = TargetPointer.Null;
78+
if (id < idDispenserObj.HighestId)
79+
threadPtr = _target.ReadPointer(idDispenserObj.IdToThread + (ulong)(id * _target.PointerSize));
80+
return threadPtr;
81+
}
82+
7183
private TargetPointer GetThreadFromLink(TargetPointer threadLink)
7284
{
7385
if (threadLink == TargetPointer.Null)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.Diagnostics.DataContractReader.Data;
7+
8+
internal sealed class IdDispenser : IData<IdDispenser>
9+
{
10+
static IdDispenser IData<IdDispenser>.Create(Target target, TargetPointer address) => new IdDispenser(target, address);
11+
public IdDispenser(Target target, TargetPointer address)
12+
{
13+
Target.TypeInfo type = target.GetTypeInfo(DataType.IdDispenser);
14+
IdToThread = target.ReadPointer(address + (ulong)type.Fields[nameof(IdToThread)].Offset);
15+
HighestId = target.Read<uint>(address + (ulong)type.Fields[nameof(HighestId)].Offset);
16+
}
17+
18+
public TargetPointer IdToThread { get; init; }
19+
public uint HighestId { get; init; }
20+
}

src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1920,7 +1920,33 @@ int ISOSDacInterface.GetThreadData(ClrDataAddress thread, DacpThreadData* data)
19201920
return hr;
19211921
}
19221922
int ISOSDacInterface.GetThreadFromThinlockID(uint thinLockId, ClrDataAddress* pThread)
1923-
=> _legacyImpl is not null ? _legacyImpl.GetThreadFromThinlockID(thinLockId, pThread) : HResults.E_NOTIMPL;
1923+
{
1924+
int hr = HResults.S_OK;
1925+
if (pThread == null)
1926+
hr = HResults.E_INVALIDARG;
1927+
try
1928+
{
1929+
TargetPointer threadPtr = _target.Contracts.Thread.IdToThread(thinLockId);
1930+
*pThread = threadPtr.ToClrDataAddress(_target);
1931+
}
1932+
catch (System.Exception ex)
1933+
{
1934+
hr = ex.HResult;
1935+
}
1936+
#if DEBUG
1937+
if (_legacyImpl is not null)
1938+
{
1939+
ClrDataAddress pThreadLocal;
1940+
int hrLocal = _legacyImpl.GetThreadFromThinlockID(thinLockId, &pThreadLocal);
1941+
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
1942+
if (hr == HResults.S_OK)
1943+
{
1944+
Debug.Assert(*pThread == pThreadLocal);
1945+
}
1946+
}
1947+
#endif
1948+
return hr;
1949+
}
19241950
int ISOSDacInterface.GetThreadLocalModuleData(ClrDataAddress thread, uint index, void* data)
19251951
{
19261952
// CoreCLR does not use thread local modules anymore

0 commit comments

Comments
 (0)