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 659c9f08ca2ea9..5959c39df7ae15 100644 --- a/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp +++ b/src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp @@ -734,7 +734,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; @@ -744,7 +743,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/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IThread.cs index 8d30a898632f76..1f9a73a9cc4916 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,14 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; +[Flags] +public enum ThreadContextSource +{ + None = 0, + Debugger = 1, + Profiler = 2, +} + public record struct ThreadStoreData( int ThreadCount, TargetPointer FirstThread, @@ -60,6 +68,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..84f62284e89853 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 @@ -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..fc50b2bca85dc9 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 @@ -760,37 +760,11 @@ private bool IsManaged(TargetPointer ip, [NotNullWhen(true)] out CodeBlockHandle private void FillContextFromThread(IPlatformAgnosticContext context, ThreadData threadData) { - 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 | ThreadContextSource.Profiler, + context.AllContextFlags); + 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 b811041fb1a856..a00ba512e35488 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; @@ -284,4 +285,34 @@ 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 && contextSource.HasFlag(ThreadContextSource.Profiler)) + filterContext = thread.ProfilerFilterContext; + + 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.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 6448703ec5be38..af0f1c85939f27 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; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -666,11 +667,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 3accd937a1f35c..c87c5b172f31d4 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 @@ -316,10 +316,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); + } }