diff --git a/docs/design/datacontracts/Thread.md b/docs/design/datacontracts/Thread.md index f2e07c8debe428..6dc9596712f18c 100644 --- a/docs/design/datacontracts/Thread.md +++ b/docs/design/datacontracts/Thread.md @@ -5,6 +5,13 @@ This contract is for reading and iterating the threads of the process. ## APIs of contract ``` csharp +[Flags] +enum ThreadContextSource +{ + None = 0, + Debugger = 1, +} + record struct ThreadStoreData ( int ThreadCount, TargetPointer FirstThread, @@ -52,7 +59,8 @@ ThreadStoreCounts GetThreadCounts(); ThreadData GetThreadData(TargetPointer threadPointer); void GetStackLimitData(TargetPointer threadPointer, out TargetPointer stackBase, out TargetPointer stackLimit, out TargetPointer frameAddress); TargetPointer IdToThread(uint id); -TargetPointer GetThreadLocalStaticBase(TargetPointer threadPointer, int indexOffset, int indexType); +TargetPointer GetThreadLocalStaticBase(TargetPointer threadPointer, TargetPointer tlsIndexPtr); +byte[] GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags); ``` ## Version 1 @@ -107,6 +115,7 @@ The contract additionally depends on these data descriptors | `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` | `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` | `UEWatsonBucketTrackerBuckets` | Pointer to thread Watson buckets data (optional, Windows only) | @@ -329,4 +338,28 @@ byte[] IThread.GetWatsonBuckets(TargetPointer threadPointer) return span.ToArray(); } +byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags) +{ + // Allocate a context buffer for the target platform + IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(target); + byte[] bytes = new byte[context.Size]; + + TargetPointer filterContext = TargetPointer.Null; + + if (contextSource.HasFlag(ThreadContextSource.Debugger)) + filterContext = target.ReadPointer(threadPointer + /* Thread::DebuggerFilterContext offset */); + + if (filterContext != TargetPointer.Null) + { + // Use the filter context directly + target.ReadBuffer(filterContext, bytes); + return bytes; + } + + // Fall back to the OS thread context + ulong osId = target.ReadNUInt(threadPointer + /* Thread::OSId offset */); + target.GetThreadContext(osId, contextFlags, bytes); + return bytes; +} + ``` diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 163213084a3e3a..e25e7e01c63147 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -5886,58 +5886,10 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, { // If the filter context is NULL, then we use the true context of the thread. pContextBuffer->ContextFlags = DT_CONTEXT_ALL; - HRESULT hr = m_pTarget->GetThreadContext(pThread->GetOSThreadId(), + IfFailThrow(m_pTarget->GetThreadContext(pThread->GetOSThreadId(), pContextBuffer->ContextFlags, sizeof(DT_CONTEXT), - reinterpret_cast(pContextBuffer)); - if (hr == E_NOTIMPL) - { - // GetThreadContext is not implemented on this data target. - // That's why we have to make do with context we can obtain from Frames explicitly stored in Thread object. - // It suffices for managed debugging stackwalk. - REGDISPLAY tmpRd = {}; - T_CONTEXT tmpContext = {}; - FillRegDisplay(&tmpRd, &tmpContext); - - // Going through thread Frames and looking for first (deepest one) one that - // that has context available for stackwalking (SP and PC) - // For example: RedirectedThreadFrame, InlinedCallFrame, DynamicHelperFrame - Frame *frame = pThread->GetFrame(); - - while (frame != NULL && frame != FRAME_TOP) - { -#ifdef FEATURE_INTERPRETER - if (frame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame) - { - PTR_InterpreterFrame pInterpreterFrame = dac_cast(frame); - pInterpreterFrame->SetContextToInterpMethodContextFrame(&tmpContext); - CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); - return S_OK; - } -#endif // FEATURE_INTERPRETER - frame->UpdateRegDisplay(&tmpRd); - if (GetRegdisplaySP(&tmpRd) != 0 && GetControlPC(&tmpRd) != 0) - { - UpdateContextFromRegDisp(&tmpRd, &tmpContext); - CopyMemory(pContextBuffer, &tmpContext, sizeof(*pContextBuffer)); - pContextBuffer->ContextFlags = DT_CONTEXT_CONTROL - #if defined(TARGET_AMD64) || defined(TARGET_ARM) - | DT_CONTEXT_INTEGER // DT_CONTEXT_INTEGER is needed to include the frame register on ARM32 and AMD64 architectures - // DT_CONTEXT_CONTROL already includes the frame register for X86 and ARM64 architectures - #endif - ; - return S_OK; - } - frame = frame->Next(); - } - - // It looks like this thread is not running managed code. - ZeroMemory(pContextBuffer, sizeof(*pContextBuffer)); - } - else - { - IfFailThrow(hr); - } + reinterpret_cast(pContextBuffer))); } else { diff --git a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp index c63d28d2d1f06c..7e0b3280d3589d 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -723,7 +723,6 @@ FramePointer DacDbiInterfaceImpl::GetFramePointerWorker(StackFrameIterator * pIt } // Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame. -// @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first. HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread, const DT_CONTEXT * pContext, OUT BOOL * pResult) { DD_ENTER_MAY_THROW; @@ -733,7 +732,12 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread { DT_CONTEXT ctxLeaf; - IfFailThrow(GetContext(vmThread, &ctxLeaf)); + Thread * pThread = vmThread.GetDacPtr(); + ctxLeaf.ContextFlags = DT_CONTEXT_ALL; + IfFailThrow(m_pTarget->GetThreadContext(pThread->GetOSThreadId(), + ctxLeaf.ContextFlags, + sizeof(DT_CONTEXT), + reinterpret_cast(&ctxLeaf))); // Call a platform-specific helper to compare the two contexts. *pResult = CompareControlRegisters(pContext, &ctxLeaf); diff --git a/src/coreclr/debug/inc/amd64/primitives.h b/src/coreclr/debug/inc/amd64/primitives.h index 83afbbbba7d327..678badee1136a9 100644 --- a/src/coreclr/debug/inc/amd64/primitives.h +++ b/src/coreclr/debug/inc/amd64/primitives.h @@ -147,14 +147,12 @@ inline void CORDbgSetSP(DT_CONTEXT *context, LPVOID rsp) #define CORDbgSetFP(context, rbp) #define CORDbgGetFP(context) 0 -// compare the RIP, RSP, and RBP inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2) { LIMITED_METHOD_DAC_CONTRACT; if ((pCtx1->Rip == pCtx2->Rip) && - (pCtx1->Rsp == pCtx2->Rsp) && - (pCtx1->Rbp == pCtx2->Rbp)) + (pCtx1->Rsp == pCtx2->Rsp)) { return TRUE; } diff --git a/src/coreclr/debug/inc/arm/primitives.h b/src/coreclr/debug/inc/arm/primitives.h index 269281eb006bed..bb585412a26e02 100644 --- a/src/coreclr/debug/inc/arm/primitives.h +++ b/src/coreclr/debug/inc/arm/primitives.h @@ -117,13 +117,10 @@ inline LPVOID CORDbgGetFP(DT_CONTEXT* context) return (LPVOID)(UINT_PTR)0; } -// compare the EIP, ESP, and EBP inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2) { LIMITED_METHOD_DAC_CONTRACT; - // @ARMTODO: Sort out frame registers - if ((pCtx1->Pc == pCtx2->Pc) && (pCtx1->Sp == pCtx2->Sp)) { diff --git a/src/coreclr/debug/inc/arm64/primitives.h b/src/coreclr/debug/inc/arm64/primitives.h index 5f8b5262d993e4..cf4023e7ab2ca9 100644 --- a/src/coreclr/debug/inc/arm64/primitives.h +++ b/src/coreclr/debug/inc/arm64/primitives.h @@ -132,11 +132,8 @@ inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * { LIMITED_METHOD_DAC_CONTRACT; - // @ARMTODO: Sort out frame registers - if ((pCtx1->Pc == pCtx2->Pc) && - (pCtx1->Sp == pCtx2->Sp) && - (pCtx1->Fp == pCtx2->Fp)) + (pCtx1->Sp == pCtx2->Sp)) { return TRUE; } diff --git a/src/coreclr/debug/inc/i386/primitives.h b/src/coreclr/debug/inc/i386/primitives.h index 757614e185fa00..d0c55986ea96e1 100644 --- a/src/coreclr/debug/inc/i386/primitives.h +++ b/src/coreclr/debug/inc/i386/primitives.h @@ -108,14 +108,12 @@ inline LPVOID CORDbgGetFP(DT_CONTEXT* context) return (LPVOID)(UINT_PTR)context->Ebp; } -// compare the EIP, ESP, and EBP inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * pCtx2) { LIMITED_METHOD_DAC_CONTRACT; if ((pCtx1->Eip == pCtx2->Eip) && - (pCtx1->Esp == pCtx2->Esp) && - (pCtx1->Ebp == pCtx2->Ebp)) + (pCtx1->Esp == pCtx2->Esp)) { return TRUE; } diff --git a/src/coreclr/debug/inc/loongarch64/primitives.h b/src/coreclr/debug/inc/loongarch64/primitives.h index cfe46955fe7c8a..f6309552e5ebf3 100644 --- a/src/coreclr/debug/inc/loongarch64/primitives.h +++ b/src/coreclr/debug/inc/loongarch64/primitives.h @@ -118,11 +118,8 @@ inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * { LIMITED_METHOD_DAC_CONTRACT; - // TODO-LoongArch64: Sort out frame registers - if ((pCtx1->Pc == pCtx2->Pc) && - (pCtx1->Sp == pCtx2->Sp) && - (pCtx1->Fp == pCtx2->Fp)) + (pCtx1->Sp == pCtx2->Sp)) { return TRUE; } diff --git a/src/coreclr/debug/inc/riscv64/primitives.h b/src/coreclr/debug/inc/riscv64/primitives.h index 17ace22981c77d..ed4f15d6018a63 100644 --- a/src/coreclr/debug/inc/riscv64/primitives.h +++ b/src/coreclr/debug/inc/riscv64/primitives.h @@ -119,11 +119,8 @@ inline BOOL CompareControlRegisters(const DT_CONTEXT * pCtx1, const DT_CONTEXT * { LIMITED_METHOD_DAC_CONTRACT; - // TODO-RISCV64: Sort out frame registers - if ((pCtx1->Pc == pCtx2->Pc) && - (pCtx1->Sp == pCtx2->Sp) && - (pCtx1->Fp == pCtx2->Fp)) + (pCtx1->Sp == pCtx2->Sp)) { return TRUE; } diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 5fd9ac2e2a117a..d50c9c8814c1fd 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -45,9 +45,6 @@ CDAC_TYPE_FIELD(Thread, T_POINTER, CachedStackBase, cdac_data::CachedSta CDAC_TYPE_FIELD(Thread, T_POINTER, CachedStackLimit, cdac_data::CachedStackLimit) CDAC_TYPE_FIELD(Thread, T_POINTER, ExceptionTracker, cdac_data::ExceptionTracker) CDAC_TYPE_FIELD(Thread, T_POINTER, DebuggerFilterContext, cdac_data::DebuggerFilterContext) -#ifdef PROFILING_SUPPORTED -CDAC_TYPE_FIELD(Thread, T_POINTER, ProfilerFilterContext, cdac_data::ProfilerFilterContext) -#endif // PROFILING_SUPPORTED CDAC_TYPE_FIELD(Thread, TYPE(ObjectHandle), ExposedObject, cdac_data::ExposedObject) CDAC_TYPE_FIELD(Thread, TYPE(ObjectHandle), LastThrownObject, cdac_data::LastThrownObject) CDAC_TYPE_FIELD(Thread, T_UINT32, LastThrownObjectIsUnhandled, cdac_data::LastThrownObjectIsUnhandled) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index ade9b6ad804998..18aad8dcd2c5e3 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -3768,9 +3768,6 @@ struct cdac_data "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 PROFILING_SUPPORTED - static constexpr size_t ProfilerFilterContext = offsetof(Thread, m_pProfilerFilterContext); -#endif // PROFILING_SUPPORTED #ifndef TARGET_UNIX static constexpr size_t UEWatsonBucketTrackerBuckets = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_UEWatsonBucketTracker) + offsetof(EHWatsonBucketTracker, m_WatsonUnhandledInfo.m_pUnhandledBuckets); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs index 8d30a898632f76..08a6d8d291850c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs @@ -5,6 +5,13 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; +[Flags] +public enum ThreadContextSource +{ + None = 0, + Debugger = 1, +} + public record struct ThreadStoreData( int ThreadCount, TargetPointer FirstThread, @@ -60,6 +67,7 @@ void GetStackLimitData(TargetPointer threadPointer, out TargetPointer stackBase, TargetPointer GetThreadLocalStaticBase(TargetPointer threadPointer, TargetPointer tlsIndexPtr) => throw new NotImplementedException(); TargetPointer GetCurrentExceptionHandle(TargetPointer threadPointer) => throw new NotImplementedException(); byte[] GetWatsonBuckets(TargetPointer threadPointer) => throw new NotImplementedException(); + byte[] GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags) => throw new NotImplementedException(); } public readonly struct Thread : IThread diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs index 8ab2e2468da526..2ef6e567420edc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs @@ -31,7 +31,9 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x4d0; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 4; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs index f88f9a9ecfb62b..1f9508e517e1cb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs @@ -24,7 +24,7 @@ public enum ContextFlagsValues : uint CONTEXT_X18 = CONTEXT_ARM64 | 0x10, CONTEXT_XSTATE = CONTEXT_ARM64 | 0x20, CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT, - CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_X18, + CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS, // // This flag is set by the unwinder if it has unwound to a call @@ -38,10 +38,9 @@ public enum ContextFlagsValues : uint public readonly uint Size => 0x390; - public readonly uint DefaultContextFlags => (uint)(ContextFlagsValues.CONTEXT_CONTROL | - ContextFlagsValues.CONTEXT_INTEGER | - ContextFlagsValues.CONTEXT_FLOATING_POINT | - ContextFlagsValues.CONTEXT_DEBUG_REGISTERS); + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 31; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs index abbcd8805257a8..8fedf1cdd5a83e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs @@ -29,7 +29,9 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x1a0; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 13; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs index c2bffd2ad361b8..c952ea17a50829 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs @@ -12,7 +12,8 @@ public sealed class ContextHolder : IPlatformAgnosticContext, IEquatable Context.Size; - public uint DefaultContextFlags => Context.DefaultContextFlags; + public uint FullContextFlags => Context.FullContextFlags; + public uint AllContextFlags => Context.AllContextFlags; public int StackPointerRegister => Context.StackPointerRegister; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs index c95012b12b74a8..6d42bba8fff30d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformAgnosticContext.cs @@ -8,7 +8,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; public interface IPlatformAgnosticContext { public abstract uint Size { get; } - public abstract uint DefaultContextFlags { get; } + public abstract uint FullContextFlags { get; } + public abstract uint AllContextFlags { get; } public int StackPointerRegister { get; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs index df26023ee54a4f..01ef3f60aed42a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/IPlatformContext.cs @@ -6,7 +6,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; public interface IPlatformContext { uint Size { get; } - uint DefaultContextFlags { get; } + uint FullContextFlags { get; } + uint AllContextFlags { get; } int StackPointerRegister { get; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs index 6acf124a0d11c1..5fbe851e1b7105 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/LoongArch64Context.cs @@ -36,10 +36,9 @@ public enum ContextFlagsValues : uint public readonly uint Size => 0x320; - public readonly uint DefaultContextFlags => (uint)(ContextFlagsValues.CONTEXT_CONTROL | - ContextFlagsValues.CONTEXT_INTEGER | - ContextFlagsValues.CONTEXT_FLOATING_POINT | - ContextFlagsValues.CONTEXT_DEBUG_REGISTERS); + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 3; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs index d401d90d89cda3..e4afafa0ecfece 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/RISCV64Context.cs @@ -36,7 +36,9 @@ public enum ContextFlagsValues : uint public readonly uint Size => 0x220; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 2; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs index 505da9a8d52889..3a32e77bb76df8 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs @@ -38,7 +38,9 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x2cc; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; + public readonly uint FullContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + + public readonly uint AllContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public readonly int StackPointerRegister => 4; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index e7a3222806464b..3a09197254291a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -49,6 +49,12 @@ private record StackDataFrameHandle( bool IsActiveFrame = false) : IStackDataFrameHandle { } + private enum ContextFlags + { + Full = 0x1, + All = 0x2, + } + private class StackWalkData(IPlatformAgnosticContext context, StackWalkState state, FrameIterator frameIter, ThreadData threadData) { public IPlatformAgnosticContext Context { get; set; } = context; @@ -106,7 +112,7 @@ public StackDataFrameHandle ToDataFrame() } IEnumerable IStackWalk.CreateStackWalk(ThreadData threadData) - => CreateStackWalkCore(threadData, skipInitialFrames: false); + => CreateStackWalkCore(threadData, skipInitialFrames: false, flags: ContextFlags.All); /// /// Core stack walk implementation. @@ -121,10 +127,16 @@ IEnumerable IStackWalk.CreateStackWalk(ThreadData threadD /// Must be false for ClrDataStackWalk, which advances the cDAC and legacy DAC in /// lockstep and must yield the same frame sequence (including initial skipped frames). /// - private IEnumerable CreateStackWalkCore(ThreadData threadData, bool skipInitialFrames) + private IEnumerable CreateStackWalkCore(ThreadData threadData, bool skipInitialFrames, ContextFlags flags) { IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(_target); - FillContextFromThread(context, threadData); + uint contextFlags = flags switch + { + ContextFlags.Full => context.FullContextFlags, + ContextFlags.All => context.AllContextFlags, + _ => throw new ArgumentOutOfRangeException(nameof(flags)), + }; + FillContextFromThread(context, threadData, contextFlags); StackWalkState state = IsManaged(context.InstructionPointer, out _) ? StackWalkState.SW_FRAMELESS : StackWalkState.SW_FRAME; FrameIterator frameIterator = new(_target, threadData); @@ -176,7 +188,7 @@ private IEnumerable CreateStackWalkCore(ThreadData thread IReadOnlyList IStackWalk.WalkStackReferences(ThreadData threadData) { - IEnumerable stackFrames = CreateStackWalkCore(threadData, skipInitialFrames: true); + IEnumerable stackFrames = CreateStackWalkCore(threadData, skipInitialFrames: true, flags: ContextFlags.Full); IEnumerable frames = stackFrames.Select(AssertCorrectHandle); IEnumerable gcFrames = Filter(frames); @@ -758,39 +770,13 @@ private bool IsManaged(TargetPointer ip, [NotNullWhen(true)] out CodeBlockHandle return false; } - private void FillContextFromThread(IPlatformAgnosticContext context, ThreadData threadData) + private void FillContextFromThread(IPlatformAgnosticContext context, ThreadData threadData, uint flags) { - byte[] bytes = new byte[context.Size]; - Span buffer = new Span(bytes); - - // Match the native DacStackReferenceWalker behavior: if the thread has a - // FilterContext or ProfilerFilterContext set, use that instead of calling - // GetThreadContext. During debugger breaks, GC stress redirection, or - // profiler stack walks, these contexts hold the correct managed frame state. - Data.Thread thread = _target.ProcessedData.GetOrAdd(threadData.ThreadAddress); - - TargetPointer filterContext = thread.DebuggerFilterContext; - if (filterContext == TargetPointer.Null) - filterContext = thread.ProfilerFilterContext; - - if (filterContext != TargetPointer.Null) - { - _target.ReadBuffer(filterContext.Value, buffer); - context.FillFromBuffer(buffer); - return; - } - - // The underlying ICLRDataTarget.GetThreadContext has some variance depending on the host. - // SOS's managed implementation sets the ContextFlags to platform specific values defined in ThreadService.cs (diagnostics repo) - // SOS's native implementation keeps the ContextFlags passed into this function. - // To match the DAC behavior, the DefaultContextFlags are what the DAC passes in in DacGetThreadContext. - // In most implementations, this will be overridden by the host, but in some cases, it may not be. - if (!_target.TryGetThreadContext(threadData.OSId.Value, context.DefaultContextFlags, buffer)) - { - throw new InvalidOperationException($"GetThreadContext failed for thread {threadData.OSId.Value}"); - } - - context.FillFromBuffer(buffer); + byte[] bytes = _target.Contracts.Thread.GetContext( + threadData.ThreadAddress, + ThreadContextSource.Debugger, + flags); + context.FillFromBuffer(bytes); } private static StackDataFrameHandle AssertCorrectHandle(IStackDataFrameHandle stackDataFrameHandle) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread_1.cs index 4e2d60fc66f341..7f06982a505985 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread_1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -269,4 +270,31 @@ byte[] IThread.GetWatsonBuckets(TargetPointer threadPointer) _target.ReadBuffer(readFrom, rval); return rval; } + + byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource contextSource, uint contextFlags) + { + IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(_target); + byte[] bytes = new byte[context.Size]; + Span buffer = new Span(bytes); + + Data.Thread thread = _target.ProcessedData.GetOrAdd(threadPointer); + + TargetPointer filterContext = TargetPointer.Null; + + if (contextSource.HasFlag(ThreadContextSource.Debugger)) + filterContext = thread.DebuggerFilterContext; + + if (filterContext != TargetPointer.Null) + { + _target.ReadBuffer(filterContext.Value, buffer); + return bytes; + } + + if (!_target.TryGetThreadContext(thread.OSId.Value, contextFlags, buffer)) + { + throw new InvalidOperationException($"GetThreadContext failed for thread {thread.OSId.Value}"); + } + + return bytes; + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs index 13164ca06db7cf..7474f8b0272a31 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs @@ -34,7 +34,6 @@ 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)); - ProfilerFilterContext = target.ReadPointerFieldOrNull(address, type, nameof(ProfilerFilterContext)); CurrentCustomDebuggerNotification = target.ReadDataField(address, type, nameof(CurrentCustomDebuggerNotification)); } @@ -54,6 +53,5 @@ public Thread(Target target, TargetPointer address) public TargetPointer UEWatsonBucketTrackerBuckets { get; init; } public TargetPointer ThreadLocalDataPtr { get; init; } public TargetPointer DebuggerFilterContext { get; init; } - public TargetPointer ProfilerFilterContext { get; init; } public ObjectHandle CurrentCustomDebuggerNotification { get; init; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 54ae8e8d815648..630fa7e168f073 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; using DACF = Microsoft.Diagnostics.DataContractReader.Contracts.DebuggerAssemblyControlFlags; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -931,11 +932,80 @@ public int GetStackParameterSize(ulong controlPC, uint* pRetVal) public int GetFramePointer(nuint pSFIHandle, ulong* pRetVal) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetFramePointer(pSFIHandle, pRetVal) : HResults.E_NOTIMPL; - public int IsLeafFrame(ulong vmThread, nint pContext, Interop.BOOL* pResult) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.IsLeafFrame(vmThread, pContext, pResult) : HResults.E_NOTIMPL; + public int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult) + { + *pResult = Interop.BOOL.FALSE; + int hr = HResults.S_OK; + try + { + IPlatformAgnosticContext leafCtx = IPlatformAgnosticContext.GetContextForPlatform(_target); + uint allFlags = leafCtx.AllContextFlags; + byte[] leafContext = _target.Contracts.Thread.GetContext(new TargetPointer(vmThread), ThreadContextSource.None, allFlags); + leafCtx.FillFromBuffer(leafContext); + + // Read the given context from the native buffer. + IPlatformAgnosticContext givenCtx = IPlatformAgnosticContext.GetContextForPlatform(_target); + givenCtx.FillFromBuffer(new Span(pContext, leafContext.Length)); + + *pResult = givenCtx.StackPointer == leafCtx.StackPointer + && givenCtx.InstructionPointer == leafCtx.InstructionPointer + ? 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.IsLeafFrame(vmThread, pContext, &resultLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + Debug.Assert(*pResult == resultLocal, $"cDAC: {*pResult}, DAC: {resultLocal}"); + } +#endif + return hr; + } - public int GetContext(ulong vmThread, nint pContextBuffer) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetContext(vmThread, pContextBuffer) : HResults.E_NOTIMPL; + public int GetContext(ulong vmThread, byte* pContextBuffer) + { + int hr = HResults.S_OK; + try + { + uint allFlags = IPlatformAgnosticContext.GetContextForPlatform(_target).AllContextFlags; + byte[] context = _target.Contracts.Thread.GetContext(new TargetPointer(vmThread), ThreadContextSource.Debugger, allFlags); + + context.AsSpan().CopyTo(new Span(pContextBuffer, context.Length)); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size; + byte[] localContextBuf = new byte[contextSize]; + fixed (byte* pLocal = localContextBuf) + { + int hrLocal = _legacy.GetContext(vmThread, pLocal); + Debug.ValidateHResult(hr, hrLocal); + + if (hr == HResults.S_OK) + { + IPlatformAgnosticContext contextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); + IPlatformAgnosticContext localContextStruct = IPlatformAgnosticContext.GetContextForPlatform(_target); + contextStruct.FillFromBuffer(new Span(pContextBuffer, (int)contextSize)); + localContextStruct.FillFromBuffer(localContextBuf); + + Debug.Assert(contextStruct.Equals(localContextStruct)); + } + } + } +#endif + return hr; + } public int ConvertContextToDebuggerRegDisplay(nint pInContext, nint pOutDRD, Interop.BOOL fActive) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.ConvertContextToDebuggerRegDisplay(pInContext, pOutDRD, fActive) : HResults.E_NOTIMPL; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index 7b3830280d2390..4f4888e6fcf11f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -323,10 +323,10 @@ public unsafe partial interface IDacDbiInterface int GetFramePointer(nuint pSFIHandle, ulong* pRetVal); [PreserveSig] - int IsLeafFrame(ulong vmThread, nint pContext, Interop.BOOL* pResult); + int IsLeafFrame(ulong vmThread, byte* pContext, Interop.BOOL* pResult); [PreserveSig] - int GetContext(ulong vmThread, nint pContextBuffer); + int GetContext(ulong vmThread, byte* pContextBuffer); [PreserveSig] int ConvertContextToDebuggerRegDisplay(nint pInContext, nint pOutDRD, Interop.BOOL fActive); diff --git a/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs new file mode 100644 index 00000000000000..14b061abe0fc8e --- /dev/null +++ b/src/native/managed/cdac/tests/DumpTests/DacDbi/DacDbiStackWalkDumpTests.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; +using Microsoft.Diagnostics.DataContractReader.Legacy; +using Xunit; + +namespace Microsoft.Diagnostics.DataContractReader.DumpTests; + +/// +/// Dump-based integration tests for DacDbiImpl stack walk methods (IsLeafFrame, GetContext). +/// Uses the StackWalk debuggee (full dump). +/// +public class DacDbiStackWalkDumpTests : DumpTestBase +{ + protected override string DebuggeeName => "StackWalk"; + protected override string DumpType => "full"; + + private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null); + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public unsafe void GetContext_Succeeds_ForCrashingThread(TestConfiguration config) + { + InitializeDumpTest(config); + DacDbiImpl dbi = CreateDacDbi(); + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(Target).Size; + byte[] contextBuffer = new byte[contextSize]; + + fixed (byte* pContext = contextBuffer) + { + int hr = dbi.GetContext(crashingThread.ThreadAddress, pContext); + Assert.Equal(System.HResults.S_OK, hr); + } + + IPlatformAgnosticContext ctx = IPlatformAgnosticContext.GetContextForPlatform(Target); + ctx.FillFromBuffer(contextBuffer); + Assert.NotEqual(TargetPointer.Null, ctx.InstructionPointer); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public unsafe void GetContext_MatchesContractGetContext(TestConfiguration config) + { + InitializeDumpTest(config); + DacDbiImpl dbi = CreateDacDbi(); + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + uint contextSize = IPlatformAgnosticContext.GetContextForPlatform(Target).Size; + + byte[] dbiContextBuffer = new byte[contextSize]; + fixed (byte* pContext = dbiContextBuffer) + { + int hr = dbi.GetContext(crashingThread.ThreadAddress, pContext); + Assert.Equal(System.HResults.S_OK, hr); + } + + uint allFlags = IPlatformAgnosticContext.GetContextForPlatform(Target).AllContextFlags; + byte[] contractContext = Target.Contracts.Thread.GetContext(crashingThread.ThreadAddress, ThreadContextSource.Debugger, allFlags); + + IPlatformAgnosticContext dbiCtx = IPlatformAgnosticContext.GetContextForPlatform(Target); + IPlatformAgnosticContext contractCtx = IPlatformAgnosticContext.GetContextForPlatform(Target); + dbiCtx.FillFromBuffer(dbiContextBuffer); + contractCtx.FillFromBuffer(contractContext); + + Assert.Equal(contractCtx.InstructionPointer, dbiCtx.InstructionPointer); + Assert.Equal(contractCtx.StackPointer, dbiCtx.StackPointer); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public unsafe void IsLeafFrame_TrueForLeafContext(TestConfiguration config) + { + InitializeDumpTest(config); + DacDbiImpl dbi = CreateDacDbi(); + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + uint allFlags = IPlatformAgnosticContext.GetContextForPlatform(Target).AllContextFlags; + byte[] leafContext = Target.Contracts.Thread.GetContext(crashingThread.ThreadAddress, ThreadContextSource.None, allFlags); + + Interop.BOOL result; + fixed (byte* pContext = leafContext) + { + int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, pContext, &result); + Assert.Equal(System.HResults.S_OK, hr); + } + + Assert.Equal(Interop.BOOL.TRUE, result); + } + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public unsafe void IsLeafFrame_FalseForNonLeafContext(TestConfiguration config) + { + InitializeDumpTest(config); + DacDbiImpl dbi = CreateDacDbi(); + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + + uint allFlags = IPlatformAgnosticContext.GetContextForPlatform(Target).AllContextFlags; + byte[] leafContext = Target.Contracts.Thread.GetContext(crashingThread.ThreadAddress, ThreadContextSource.None, allFlags); + IPlatformAgnosticContext leafCtx = IPlatformAgnosticContext.GetContextForPlatform(Target); + leafCtx.FillFromBuffer(leafContext); + + IStackWalk sw = Target.Contracts.StackWalk; + + // Find a frame whose SP+IP differs from the leaf context + byte[]? nonLeafContext = sw.CreateStackWalk(crashingThread) + .Select(sw.GetRawContext) + .FirstOrDefault(ctx => + { + IPlatformAgnosticContext frameCtx = IPlatformAgnosticContext.GetContextForPlatform(Target); + frameCtx.FillFromBuffer(ctx); + return frameCtx.StackPointer != leafCtx.StackPointer + || frameCtx.InstructionPointer != leafCtx.InstructionPointer; + }); + + Assert.NotNull(nonLeafContext); + + Interop.BOOL result; + fixed (byte* pContext = nonLeafContext) + { + int hr = dbi.IsLeafFrame(crashingThread.ThreadAddress, pContext, &result); + Assert.Equal(System.HResults.S_OK, hr); + } + + Assert.Equal(Interop.BOOL.FALSE, result); + } +} diff --git a/src/native/managed/cdac/tests/DumpTests/StackWalkDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/StackWalkDumpTests.cs index 65c0b9597ffa8f..d5a867c06f050f 100644 --- a/src/native/managed/cdac/tests/DumpTests/StackWalkDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/StackWalkDumpTests.cs @@ -332,4 +332,25 @@ public unsafe void VarargPInvoke_GetCodeHeaderDataWithInvalidPrecodeAddress(Test Assert.Fail("Expected to find a frame with a valid entry point"); } + + // ========== GetContext API tests ========== + + [ConditionalTheory] + [MemberData(nameof(TestConfigurations))] + [SkipOnVersion("net10.0", "InlinedCallFrame.Datum was added after net10.0")] + public void GetContext_ReturnsNonEmptyContext(TestConfiguration config) + { + InitializeDumpTest(config); + + ThreadData crashingThread = DumpTestHelpers.FindFailFastThread(Target); + uint allFlags = Contracts.StackWalkHelpers.IPlatformAgnosticContext.GetContextForPlatform(Target).AllContextFlags; + byte[] context = Target.Contracts.Thread.GetContext(crashingThread.ThreadAddress, ThreadContextSource.None, allFlags); + + Assert.NotNull(context); + Assert.True(context.Length > 0, "Expected non-empty context"); + + var ctx = Contracts.StackWalkHelpers.IPlatformAgnosticContext.GetContextForPlatform(Target); + ctx.FillFromBuffer(context); + Assert.NotEqual(TargetPointer.Null, ctx.InstructionPointer); + } } diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.Thread.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.Thread.cs index bfa7b2a8b5416b..cf6dd55ee67f3b 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.Thread.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.Thread.cs @@ -187,9 +187,8 @@ internal sealed class MockThread : TypedView private const string ThreadLocalDataPtrFieldName = "ThreadLocalDataPtr"; private const string UEWatsonBucketTrackerBucketsFieldName = "UEWatsonBucketTrackerBuckets"; private const string DebuggerFilterContextFieldName = "DebuggerFilterContext"; - private const string ProfilerFilterContextFieldName = "ProfilerFilterContext"; - public static Layout CreateLayout(MockTarget.Architecture architecture, bool hasProfilingSupport = true) + public static Layout CreateLayout(MockTarget.Architecture architecture) { SequentialLayoutBuilder layoutBuilder = new SequentialLayoutBuilder("Thread", architecture) .AddUInt32Field(IdFieldName) @@ -210,11 +209,6 @@ public static Layout CreateLayout(MockTarget.Architecture architectu .AddPointerField(UEWatsonBucketTrackerBucketsFieldName) .AddPointerField(DebuggerFilterContextFieldName); - if (hasProfilingSupport) - { - layoutBuilder.AddPointerField(ProfilerFilterContextFieldName); - } - return layoutBuilder.Build(); } @@ -307,19 +301,19 @@ internal sealed class MockThreadBuilder private MockThread? _previousThread; - public MockThreadBuilder(MockMemorySpace.Builder builder, bool hasProfilingSupport = true) - : this(builder, (DefaultAllocationRangeStart, DefaultAllocationRangeEnd), hasProfilingSupport) + public MockThreadBuilder(MockMemorySpace.Builder builder) + : this(builder, (DefaultAllocationRangeStart, DefaultAllocationRangeEnd)) { } - public MockThreadBuilder(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocationRange, bool hasProfilingSupport = true) + public MockThreadBuilder(MockMemorySpace.Builder builder, (ulong Start, ulong End) allocationRange) { Builder = builder; _allocator = Builder.CreateAllocator(allocationRange.Start, allocationRange.End); TargetTestHelpers helpers = builder.TargetTestHelpers; ExceptionInfoLayout = MockExceptionInfo.CreateLayout(helpers.Arch); - ThreadLayout = MockThread.CreateLayout(helpers.Arch, hasProfilingSupport); + ThreadLayout = MockThread.CreateLayout(helpers.Arch); ThreadStoreLayout = MockThreadStore.CreateLayout(helpers.Arch); GCAllocContextLayout = MockGCAllocContext.CreateLayout(helpers.Arch); EEAllocContextLayout = MockEEAllocContext.CreateLayout(helpers.Arch, GCAllocContextLayout); diff --git a/src/native/managed/cdac/tests/ThreadTests.cs b/src/native/managed/cdac/tests/ThreadTests.cs index a9960aaf5d9c0b..d314c9f5cf2432 100644 --- a/src/native/managed/cdac/tests/ThreadTests.cs +++ b/src/native/managed/cdac/tests/ThreadTests.cs @@ -12,11 +12,10 @@ public unsafe class ThreadTests { private static TestPlaceholderTarget CreateTarget( MockTarget.Architecture arch, - Action configure, - bool hasProfilingSupport = true) + Action configure) { TestPlaceholderTarget.Builder targetBuilder = new(arch); - MockThreadBuilder threadBuilder = new(targetBuilder.MemoryBuilder, hasProfilingSupport: hasProfilingSupport); + MockThreadBuilder threadBuilder = new(targetBuilder.MemoryBuilder); configure(threadBuilder); TestPlaceholderTarget target = targetBuilder @@ -277,25 +276,4 @@ public void GetCurrentExceptionHandle_HandlePointsToNull(MockTarget.Architecture TargetPointer thrownObjectHandle = contract.GetCurrentExceptionHandle(new TargetPointer(thread!.Address)); Assert.Equal(TargetPointer.Null, thrownObjectHandle); } - - [Theory] - [ClassData(typeof(MockTarget.StdArch))] - public void GetThreadData_NoProfilerFilterContext(MockTarget.Architecture arch) - { - const uint id = 1; - const ulong osId = 1234; - MockThread? thread = null; - - TestPlaceholderTarget target = CreateTarget( - arch, - threadBuilder => thread = threadBuilder.AddThread(id, osId), - hasProfilingSupport: false); - - IThread contract = target.Contracts.Thread; - Assert.NotNull(contract); - - ThreadData data = contract.GetThreadData(new TargetPointer(thread!.Address)); - Assert.Equal(id, data.Id); - Assert.Equal(new TargetNUInt(osId), data.OSId); - } }