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
8 changes: 8 additions & 0 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum ThreadState
Unstarted = 0x00000400, // Thread has never been started
Stopped = 0x00010000, // Thread has started to shut down
ThreadPoolWorker = 0x01000000, // is this a threadpool worker thread?
Detached = unchecked((int)0x80000000), // Thread was detached
}

record struct ThreadData (
Expand All @@ -36,7 +37,11 @@ record struct ThreadData (
TargetPointer AllocContextLimit;
TargetPointer Frame;
TargetPointer FirstNestedException;
TargetPointer ExposedObjectHandle;
TargetPointer LastThrownObjectHandle;
TargetPointer CurrentCustomDebuggerNotificationHandle;
bool LastThrownObjectIsUnhandled;
bool HasUnhandledException;
TargetPointer NextThread;
);
```
Expand Down Expand Up @@ -96,7 +101,10 @@ The contract additionally depends on these data descriptors
| `Thread` | `Frame` | Pointer to current frame |
| `Thread` | `CachedStackBase` | Pointer to the base of the stack |
| `Thread` | `CachedStackLimit` | Pointer to the limit of the stack |
| `Thread` | `ExposedObject` | Handle to the managed `Thread` object exposed to the debugger |
| `Thread` | `LastThrownObject` | Handle to last thrown exception object |
| `Thread` | `LastThrownObjectIsUnhandled` | Whether `LastThrownObject` should be treated as unhandled |
| `Thread` | `CurrentCustomDebuggerNotification` | Handle to the current custom debugger notification object |
| `Thread` | `LinkNext` | Pointer to get next thread |
| `Thread` | `ExceptionTracker` | Pointer to exception tracking information |
| `Thread` | `RuntimeThreadLocals` | Pointer to some thread-local storage |
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ CDAC_TYPE_FIELD(Thread, T_POINTER, DebuggerFilterContext, cdac_data<Thread>::Deb
#ifdef PROFILING_SUPPORTED
CDAC_TYPE_FIELD(Thread, T_POINTER, ProfilerFilterContext, cdac_data<Thread>::ProfilerFilterContext)
#endif // PROFILING_SUPPORTED
CDAC_TYPE_FIELD(Thread, TYPE(GCHandle), GCHandle, cdac_data<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, T_POINTER, ExposedObject, cdac_data<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, T_POINTER, LastThrownObject, cdac_data<Thread>::LastThrownObject)
CDAC_TYPE_FIELD(Thread, T_UINT32, LastThrownObjectIsUnhandled, cdac_data<Thread>::LastThrownObjectIsUnhandled)
CDAC_TYPE_FIELD(Thread, T_POINTER, LinkNext, cdac_data<Thread>::Link)
CDAC_TYPE_FIELD(Thread, T_POINTER, ThreadLocalDataPtr, cdac_data<Thread>::ThreadLocalDataPtr)
CDAC_TYPE_FIELD(Thread, T_POINTER, CurrentCustomDebuggerNotification, cdac_data<Thread>::CurrentCustomDebuggerNotification)
#ifndef TARGET_UNIX
CDAC_TYPE_FIELD(Thread, T_POINTER, UEWatsonBucketTrackerBuckets, cdac_data<Thread>::UEWatsonBucketTrackerBuckets)
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -3760,8 +3760,10 @@ struct cdac_data<Thread>
static constexpr size_t CachedStackLimit = offsetof(Thread, m_CacheStackLimit);
static constexpr size_t ExposedObject = offsetof(Thread, m_ExposedObject);
static constexpr size_t LastThrownObject = offsetof(Thread, m_LastThrownObjectHandle);
static constexpr size_t LastThrownObjectIsUnhandled = offsetof(Thread, m_ltoIsUnhandled);
static constexpr size_t Link = offsetof(Thread, m_Link);
static constexpr size_t ThreadLocalDataPtr = offsetof(Thread, m_ThreadLocalDataPtr);
static constexpr size_t CurrentCustomDebuggerNotification = offsetof(Thread, m_hCurrNotification);

static_assert(std::is_same<decltype(std::declval<Thread>().m_ExceptionState), ThreadExceptionState>::value,
"Thread::m_ExceptionState is of type ThreadExceptionState");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public enum ThreadState
Unstarted = 0x00000400, // Thread has never been started
Stopped = 0x00010000, // Thread has started to shut down
ThreadPoolWorker = 0x01000000, // Thread is a thread pool worker thread
Detached = unchecked((int)0x80000000), // Thread was detached
}

public record struct ThreadData(
Expand All @@ -38,7 +39,11 @@ public record struct ThreadData(
TargetPointer AllocContextLimit,
TargetPointer Frame,
TargetPointer FirstNestedException,
TargetPointer ExposedObjectHandle,
TargetPointer LastThrownObjectHandle,
TargetPointer CurrentCustomDebuggerNotificationHandle,
bool LastThrownObjectIsUnhandled,
bool HasUnhandledException,
TargetPointer NextThread);

public interface IThread : IContract
Expand All @@ -54,7 +59,6 @@ void GetStackLimitData(TargetPointer threadPointer, out TargetPointer stackBase,
TargetPointer IdToThread(uint id) => throw new NotImplementedException();
TargetPointer GetThreadLocalStaticBase(TargetPointer threadPointer, TargetPointer tlsIndexPtr) => throw new NotImplementedException();
TargetPointer GetCurrentExceptionHandle(TargetPointer threadPointer) => throw new NotImplementedException();
TargetPointer GetThrowableObject(TargetPointer threadPointer) => throw new NotImplementedException();
byte[] GetWatsonBuckets(TargetPointer threadPointer) => throw new NotImplementedException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Microsoft.Diagnostics.DataContractReader;
public static class CorDbgHResults
{
public const int CORDBG_E_NOTREADY = unchecked((int)0x80131c10);
public const int CORDBG_E_BAD_THREAD_STATE = unchecked((int)0x8013132d);
public const int CORDBG_E_READVIRTUAL_FAILURE = unchecked((int)0x80131c49);
public const int ERROR_BUFFER_OVERFLOW = unchecked((int)0x8007006F); // HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW)
public const int CORDBG_E_CLASS_NOT_LOADED = unchecked((int)0x80131303);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ private enum ThreadState_1
Background = 0x200,
Unstarted = 0x400,
Stopped = 0x10000,
ThreadPoolWorker = 0x1000000
ThreadPoolWorker = 0x1000000,
Detached = unchecked((int)0x80000000)
}

[Flags]
private enum ExceptionFlags
{
DebuggerInterceptInfo = 0x00000200,
IsUnhandled = 0x00000800,
}

internal Thread_1(Target target)
Expand Down Expand Up @@ -73,6 +81,8 @@ private static Contracts.ThreadState GetThreadState(ThreadState_1 state)
result |= Contracts.ThreadState.Stopped;
if (state.HasFlag(ThreadState_1.ThreadPoolWorker))
result |= Contracts.ThreadState.ThreadPoolWorker;
if (state.HasFlag(ThreadState_1.Detached))
result |= Contracts.ThreadState.Detached;
return result;
}

Expand All @@ -82,12 +92,23 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)

TargetPointer address = _target.ReadPointer(thread.ExceptionTracker);
TargetPointer firstNestedException = TargetPointer.Null;
bool hasUnhandledException = false;
if (address != TargetPointer.Null)
{
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(address);
firstNestedException = exceptionInfo.PreviousNestedInfo;

if (exceptionInfo.ThrownObjectHandle != TargetPointer.Null)
{
uint exceptionFlags = exceptionInfo.ExceptionFlags;
hasUnhandledException = (exceptionFlags & (uint)ExceptionFlags.IsUnhandled) != 0
&& (exceptionFlags & (uint)ExceptionFlags.DebuggerInterceptInfo) == 0;
}
}

if (thread.LastThrownObjectIsUnhandled != 0)
hasUnhandledException = true;

return new ThreadData(
threadPointer,
thread.Id,
Expand All @@ -98,7 +119,11 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer)
thread.RuntimeThreadLocals?.AllocContext.GCAllocationContext.Limit ?? TargetPointer.Null,
thread.Frame,
firstNestedException,
thread.ExposedObject,
thread.LastThrownObject.Handle,
thread.CurrentCustomDebuggerNotification,
thread.LastThrownObjectIsUnhandled != 0,
hasUnhandledException,
GetThreadFromLink(thread.LinkNext));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public Thread(Target target, TargetPointer address)
CachedStackBase = target.ReadPointerField(address, type, nameof(CachedStackBase));
CachedStackLimit = target.ReadPointerField(address, type, nameof(CachedStackLimit));

ExposedObject = target.ReadPointerField(address, type, nameof(ExposedObject));
LastThrownObject = target.ProcessedData.GetOrAdd<ObjectHandle>(
target.ReadPointerField(address, type, nameof(LastThrownObject)));
LastThrownObjectIsUnhandled = target.ReadField<uint>(address, type, nameof(LastThrownObjectIsUnhandled));
LinkNext = target.ReadPointerField(address, type, nameof(LinkNext));

// Address of the exception tracker
Expand All @@ -34,6 +36,7 @@ public Thread(Target target, TargetPointer address)
ThreadLocalDataPtr = target.ReadPointerField(address, type, nameof(ThreadLocalDataPtr));
DebuggerFilterContext = target.ReadPointerField(address, type, nameof(DebuggerFilterContext));
ProfilerFilterContext = target.ReadPointerFieldOrNull(address, type, nameof(ProfilerFilterContext));
CurrentCustomDebuggerNotification = target.ReadPointerField(address, type, nameof(CurrentCustomDebuggerNotification));
}

public uint Id { get; init; }
Expand All @@ -44,11 +47,14 @@ public Thread(Target target, TargetPointer address)
public TargetPointer Frame { get; init; }
public TargetPointer CachedStackBase { get; init; }
public TargetPointer CachedStackLimit { get; init; }
public TargetPointer ExposedObject { get; init; }
public ObjectHandle LastThrownObject { get; init; }
public uint LastThrownObjectIsUnhandled { get; init; }
public TargetPointer LinkNext { get; init; }
public TargetPointer ExceptionTracker { get; init; }
public TargetPointer UEWatsonBucketTrackerBuckets { get; init; }
public TargetPointer ThreadLocalDataPtr { get; init; }
public TargetPointer DebuggerFilterContext { get; init; }
public TargetPointer ProfilerFilterContext { get; init; }
public TargetPointer CurrentCustomDebuggerNotification { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,33 @@ public int GetThreadHandle(ulong vmThread, nint pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadHandle(vmThread, pRetVal) : HResults.E_NOTIMPL;

public int GetThreadObject(ulong vmThread, ulong* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadObject(vmThread, pRetVal) : HResults.E_NOTIMPL;
{
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread));
if ((threadData.State & (Contracts.ThreadState.Stopped | Contracts.ThreadState.Unstarted | Contracts.ThreadState.Detached)) != 0)
throw Marshal.GetExceptionForHR(CorDbgHResults.CORDBG_E_BAD_THREAD_STATE)!;

*pRetVal = threadData.ExposedObjectHandle.Value;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong retValLocal;
int hrLocal = _legacy.GetThreadObject(vmThread, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal:x}, DAC: {retValLocal:x}");
}
#endif
return hr;
}

public int GetThreadAllocInfo(ulong vmThread, DacDbiThreadAllocInfo* pThreadAllocInfo)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetThreadAllocInfo(vmThread, pThreadAllocInfo) : HResults.E_NOTIMPL;
Expand All @@ -390,7 +416,30 @@ public int SetDebugState(ulong vmThread, int debugState)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.SetDebugState(vmThread, debugState) : HResults.E_NOTIMPL;

public int HasUnhandledException(ulong vmThread, Interop.BOOL* pResult)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.HasUnhandledException(vmThread, pResult) : HResults.E_NOTIMPL;
{
*pResult = Interop.BOOL.FALSE;
int hr = HResults.S_OK;
try
{
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread));
*pResult = threadData.HasUnhandledException ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
Interop.BOOL resultLocal;
int hrLocal = _legacy.HasUnhandledException(vmThread, &resultLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}");
}
#endif
return hr;
}

public int GetUserState(ulong vmThread, int* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetUserState(vmThread, pRetVal) : HResults.E_NOTIMPL;
Expand Down Expand Up @@ -493,8 +542,15 @@ public int GetCurrentException(ulong vmThread, ulong* pRetVal)
int hr = HResults.S_OK;
try
{
TargetPointer throwable = _target.Contracts.Thread.GetThrowableObject(new TargetPointer(vmThread));
*pRetVal = throwable.Value;
TargetPointer threadPtr = new TargetPointer(vmThread);
TargetPointer exceptionHandle = _target.Contracts.Thread.GetCurrentExceptionHandle(threadPtr);
if (exceptionHandle == TargetPointer.Null)
{
ThreadData data = _target.Contracts.Thread.GetThreadData(threadPtr);
if (data.LastThrownObjectIsUnhandled)
exceptionHandle = data.LastThrownObjectHandle;
}
*pRetVal = exceptionHandle.Value;
}
catch (System.Exception ex)
{
Expand All @@ -517,7 +573,30 @@ public int GetObjectForCCW(ulong ccwPtr, ulong* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectForCCW(ccwPtr, pRetVal) : HResults.E_NOTIMPL;

public int GetCurrentCustomDebuggerNotification(ulong vmThread, ulong* pRetVal)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetCurrentCustomDebuggerNotification(vmThread, pRetVal) : HResults.E_NOTIMPL;
{
*pRetVal = 0;
int hr = HResults.S_OK;
try
{
Contracts.ThreadData threadData = _target.Contracts.Thread.GetThreadData(new TargetPointer(vmThread));
*pRetVal = threadData.CurrentCustomDebuggerNotificationHandle.Value;
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacy is not null)
{
ulong retValLocal;
int hrLocal = _legacy.GetCurrentCustomDebuggerNotification(vmThread, &retValLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pRetVal == retValLocal, $"cDAC: {*pRetVal:x}, DAC: {retValLocal:x}");
}
#endif
return hr;
}

public int GetCurrentAppDomain(ulong* pRetVal)
{
Expand Down
8 changes: 8 additions & 0 deletions src/native/managed/cdac/tests/ClrDataExceptionStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ private static (TestPlaceholderTarget Target, IXCLRDataTask Task) CreateTargetWi
AllocContextLimit: TargetPointer.Null,
Frame: TargetPointer.Null,
FirstNestedException: firstNestedException,
ExposedObjectHandle: TargetPointer.Null,
LastThrownObjectHandle: lastThrownObjectHandle,
CurrentCustomDebuggerNotificationHandle: TargetPointer.Null,
LastThrownObjectIsUnhandled: false,
HasUnhandledException: false,
NextThread: TargetPointer.Null));

var target = new TestPlaceholderTarget.Builder(arch)
Expand Down Expand Up @@ -461,7 +465,11 @@ private static (IXCLRDataTask Task, string ExpectedMessage) CreateTargetWithLast
AllocContextLimit: TargetPointer.Null,
Frame: TargetPointer.Null,
FirstNestedException: firstNestedException,
ExposedObjectHandle: TargetPointer.Null,
LastThrownObjectHandle: lastThrownObjectHandle,
CurrentCustomDebuggerNotificationHandle: TargetPointer.Null,
LastThrownObjectIsUnhandled: false,
HasUnhandledException: false,
NextThread: TargetPointer.Null));

var mockException = new Mock<IException>();
Expand Down
Loading
Loading