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
3 changes: 3 additions & 0 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ enum ThreadState
}

record struct ThreadData (
TargetPointer ThreadAddress,
uint Id;
TargetNUInt OSId;
ThreadState State;
Expand All @@ -51,6 +52,7 @@ record struct ThreadData (
bool LastThrownObjectIsUnhandled;
bool HasUnhandledException;
Comment thread
rcj1 marked this conversation as resolved.
TargetPointer NextThread;
TargetPointer ThreadHandle;
);
```

Expand Down Expand Up @@ -131,6 +133,7 @@ The contract additionally depends on these data descriptors
| `Thread` | `DebuggerFilterContext` | Pointer to the debugger filter context for the thread |
| `Thread` | `RuntimeThreadLocals` | Pointer to some thread-local storage |
| `Thread` | `ThreadLocalDataPtr` | Pointer to thread local data structure |
| `Thread` | `ThreadHandle` | OS thread handle (optional, Windows only; readers should expect `TargetPointer.Null` on non-Windows targets) |
| `Thread` | `UEWatsonBucketTrackerBuckets` | Pointer to thread Watson buckets data (optional, Windows only) |
| `ThreadLocalData` | `NonCollectibleTlsData` | Count of non-collectible TLS data entries |
| `ThreadLocalData` | `NonCollectibleTlsArrayData` | Pointer to non-collectible TLS array data |
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4499,9 +4499,12 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetThreadHandle(VMPTR_Thread vmTh
HRESULT hr = S_OK;
EX_TRY
{

#ifdef TARGET_WINDOWS
Thread * pThread = vmThread.GetDacPtr();
*pRetVal = pThread->GetThreadHandle();
#else
*pRetVal = NULL;
#endif // TARGET_WINDOWS
}
EX_CATCH_HRESULT(hr);
return hr;
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ CDAC_TYPE_FIELD(Thread, T_POINTER, CachedStackLimit, cdac_data<Thread>::CachedSt
CDAC_TYPE_FIELD(Thread, T_POINTER, ExceptionTracker, cdac_data<Thread>::ExceptionTracker)
CDAC_TYPE_FIELD(Thread, T_UINT32, DebuggerControlledThreadState, cdac_data<Thread>::DebuggerControlledThreadState)
CDAC_TYPE_FIELD(Thread, T_POINTER, DebuggerFilterContext, cdac_data<Thread>::DebuggerFilterContext)
#ifdef TARGET_WINDOWS
CDAC_TYPE_FIELD(Thread, T_POINTER, ThreadHandle, cdac_data<Thread>::ThreadHandle)
#endif // TARGET_WINDOWS
CDAC_TYPE_FIELD(Thread, TYPE(ObjectHandle), ExposedObject, cdac_data<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, TYPE(ObjectHandle), LastThrownObject, cdac_data<Thread>::LastThrownObject)
CDAC_TYPE_FIELD(Thread, T_UINT32, LastThrownObjectIsUnhandled, cdac_data<Thread>::LastThrownObjectIsUnhandled)
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,9 @@ struct cdac_data<Thread>
"Thread::m_ExceptionState is of type ThreadExceptionState");
static constexpr size_t ExceptionTracker = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_pCurrentTracker);
static constexpr size_t DebuggerFilterContext = offsetof(Thread, m_debuggerFilterContext);
#ifdef TARGET_WINDOWS
static constexpr size_t ThreadHandle = offsetof(Thread, m_ThreadHandle);
Comment thread
rcj1 marked this conversation as resolved.
#endif
#ifndef TARGET_UNIX
static constexpr size_t UEWatsonBucketTrackerBuckets = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_UEWatsonBucketTracker)
+ offsetof(EHWatsonBucketTracker, m_WatsonUnhandledInfo.m_pUnhandledBuckets);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public record struct ThreadData(
TargetPointer CurrentCustomDebuggerNotificationHandle,
bool LastThrownObjectIsUnhandled,
bool HasUnhandledException,
TargetPointer NextThread);
TargetPointer NextThread,
TargetPointer ThreadHandle);
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
max-charlamb marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.

public interface IThread : IContract
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
thread.CurrentCustomDebuggerNotification.Handle,
thread.LastThrownObjectIsUnhandled != 0,
hasUnhandledException,
thread.LinkNext);
thread.LinkNext,
thread.ThreadHandle);
}

void IThread.GetThreadAllocContext(TargetPointer threadPointer, out long allocBytes, out long allocBytesLoh)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public Thread(Target target, TargetPointer address)
UEWatsonBucketTrackerBuckets = target.ReadPointerFieldOrNull(address, type, nameof(UEWatsonBucketTrackerBuckets));
ThreadLocalDataPtr = target.ReadPointerField(address, type, nameof(ThreadLocalDataPtr));
DebuggerFilterContext = target.ReadPointerField(address, type, nameof(DebuggerFilterContext));
Comment thread
rcj1 marked this conversation as resolved.
ThreadHandle = target.ReadPointerFieldOrNull(address, type, nameof(ThreadHandle));
CurrentCustomDebuggerNotification = target.ReadDataField<ObjectHandle>(address, type, nameof(CurrentCustomDebuggerNotification));
}

Expand All @@ -54,5 +55,6 @@ public Thread(Target target, TargetPointer address)
public TargetPointer UEWatsonBucketTrackerBuckets { get; init; }
public TargetPointer ThreadLocalDataPtr { get; init; }
public TargetPointer DebuggerFilterContext { get; init; }
public TargetPointer ThreadHandle { get; init; }
public ObjectHandle CurrentCustomDebuggerNotification { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -725,8 +725,30 @@ public int IsThreadMarkedDead(ulong vmThread, Interop.BOOL* pResult)
return hr;
}

public int GetThreadHandle(ulong vmThread, nint pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadHandle(vmThread, pRetVal) : HResults.E_NOTIMPL;
public int GetThreadHandle(ulong vmThread, void** pRetVal)
{
int hr = HResults.S_OK;
try
{
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread));
*pRetVal = (void*)threadData.ThreadHandle.Value;
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
}
Comment thread
rcj1 marked this conversation as resolved.
catch (System.Exception ex)
{
hr = ex.HResult;
}
Comment thread
rcj1 marked this conversation as resolved.
#if DEBUG
if (_legacy is not null)
{
void* retValLocal = null;
int hrLocal = _legacy.GetThreadHandle(vmThread, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {(nuint)(*pRetVal):x}, DAC: {(nuint)retValLocal:x}");
}
#endif
return hr;
}

public int GetThreadObject(ulong vmThread, ulong* pRetVal)
{
Expand Down Expand Up @@ -758,7 +780,38 @@ public int GetThreadObject(ulong vmThread, ulong* pRetVal)
}

public int GetThreadAllocInfo(ulong vmThread, DacDbiThreadAllocInfo* pThreadAllocInfo)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadAllocInfo(vmThread, pThreadAllocInfo) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
Comment thread
rcj1 marked this conversation as resolved.
try
{
TargetPointer threadPtr = new TargetPointer(vmThread);
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(threadPtr);
_target.Contracts.Thread.GetThreadAllocContext(threadPtr, out long allocBytes, out long allocBytesLoh);

ulong limit = threadData.AllocContextLimit.Value;
ulong pointer = threadData.AllocContextPointer.Value;
pThreadAllocInfo->allocBytesSOH = (ulong)allocBytes - (limit - pointer);
pThreadAllocInfo->allocBytesUOH = (ulong)allocBytesLoh;
}
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
catch (System.Exception ex)
{
hr = ex.HResult;
}
Comment thread
rcj1 marked this conversation as resolved.
#if DEBUG
if (_legacy is not null)
{
DacDbiThreadAllocInfo allocInfoLocal = default;
int hrLocal = _legacy.GetThreadAllocInfo(vmThread, &allocInfoLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
Debug.Assert(pThreadAllocInfo->allocBytesSOH == allocInfoLocal.allocBytesSOH, $"cDAC: {pThreadAllocInfo->allocBytesSOH}, DAC: {allocInfoLocal.allocBytesSOH}");
Debug.Assert(pThreadAllocInfo->allocBytesUOH == allocInfoLocal.allocBytesUOH, $"cDAC: {pThreadAllocInfo->allocBytesUOH}, DAC: {allocInfoLocal.allocBytesUOH}");
}
}
#endif
return hr;
}

public int SetDebugState(ulong vmThread, int debugState)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ public unsafe partial interface IDacDbiInterface
int IsThreadMarkedDead(ulong vmThread, Interop.BOOL* pResult);

[PreserveSig]
int GetThreadHandle(ulong vmThread, nint pRetVal);
int GetThreadHandle(ulong vmThread, void** pRetVal);

[PreserveSig]
int GetThreadObject(ulong vmThread, ulong* pRetVal);
Expand Down
6 changes: 4 additions & 2 deletions src/native/managed/cdac/tests/ClrDataExceptionStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ private static (TestPlaceholderTarget Target, IXCLRDataTask Task) CreateTargetWi
CurrentCustomDebuggerNotificationHandle: TargetPointer.Null,
LastThrownObjectIsUnhandled: false,
HasUnhandledException: false,
NextThread: TargetPointer.Null));
NextThread: TargetPointer.Null,
ThreadHandle: TargetPointer.Null));

var target = new TestPlaceholderTarget.Builder(arch)
.UseReader((ulong _, Span<byte> _) => -1)
Expand Down Expand Up @@ -470,7 +471,8 @@ private static (IXCLRDataTask Task, string ExpectedMessage) CreateTargetWithLast
CurrentCustomDebuggerNotificationHandle: TargetPointer.Null,
LastThrownObjectIsUnhandled: false,
HasUnhandledException: false,
NextThread: TargetPointer.Null));
NextThread: TargetPointer.Null,
ThreadHandle: TargetPointer.Null));

var mockException = new Mock<IException>();
mockException.Setup(e => e.GetExceptionData(exceptionObjectAddr)).Returns(new ExceptionData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ internal sealed class MockThread : TypedView
private const string LinkNextFieldName = "LinkNext";
private const string ExceptionTrackerFieldName = "ExceptionTracker";
private const string ThreadLocalDataPtrFieldName = "ThreadLocalDataPtr";
private const string ThreadHandleFieldName = "ThreadHandle";
private const string UEWatsonBucketTrackerBucketsFieldName = "UEWatsonBucketTrackerBuckets";
private const string DebuggerFilterContextFieldName = "DebuggerFilterContext";

Expand All @@ -212,6 +213,7 @@ public static Layout<MockThread> CreateLayout(MockTarget.Architecture architectu
.AddPointerField(LinkNextFieldName)
.AddPointerField(ExceptionTrackerFieldName)
.AddPointerField(ThreadLocalDataPtrFieldName)
.AddPointerField(ThreadHandleFieldName)
.AddPointerField(UEWatsonBucketTrackerBucketsFieldName)
.AddPointerField(DebuggerFilterContextFieldName);

Expand Down Expand Up @@ -297,6 +299,12 @@ public ulong LastThrownObject
get => ReadPointerField(LastThrownObjectFieldName);
set => WritePointerField(LastThrownObjectFieldName, value);
}

public ulong ThreadHandle
{
get => ReadPointerField(ThreadHandleFieldName);
set => WritePointerField(ThreadHandleFieldName, value);
}
}

internal sealed class MockThreadBuilder
Expand Down
20 changes: 20 additions & 0 deletions src/native/managed/cdac/tests/ThreadTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,26 @@ public void GetThreadData_LastThrownObjectHandle_NoActiveException(MockTarget.Ar
Assert.Equal(lastThrownHandle, data.LastThrownObjectHandle);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetThreadData_ThreadHandle(MockTarget.Architecture arch)
{
const ulong threadHandle = 0xABCD_1234;
MockThread? thread = null;

TestPlaceholderTarget target = CreateTarget(
arch,
threadBuilder =>
{
thread = threadBuilder.AddThread(1, 1234);
thread.ThreadHandle = threadHandle;
});

IThread contract = target.Contracts.Thread;
ThreadData data = contract.GetThreadData(new TargetPointer(thread!.Address));
Assert.Equal(new TargetPointer(threadHandle), data.ThreadHandle);
}

public static IEnumerable<object[]> SetDebuggerControlledThreadStateData()
{
foreach (var arch in new MockTarget.StdArch())
Expand Down
Loading