From 7782ee5ac7d2ebf3bb4de264fadc721e3161f13d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 22 May 2026 21:30:57 +0000 Subject: [PATCH 1/3] [cDAC] Restore Frame-walk fallback in Thread.GetContext when GetThreadContext is unavailable Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- docs/design/datacontracts/Thread.md | 18 ++++++-- .../Contracts/Thread_1.cs | 44 +++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/docs/design/datacontracts/Thread.md b/docs/design/datacontracts/Thread.md index b00c13924b19f9..12b1eb83cf799d 100644 --- a/docs/design/datacontracts/Thread.md +++ b/docs/design/datacontracts/Thread.md @@ -403,10 +403,22 @@ byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource conte return bytes; } - // Fall back to the OS thread context + // Try to read the OS thread context from the data target ulong osId = target.ReadNUInt(threadPointer + /* Thread::OSId offset */); - target.GetThreadContext(osId, contextFlags, bytes); - return bytes; + if (target.TryGetThreadContext(osId, contextFlags, bytes)) + return bytes; + + // GetThreadContext is not implemented on this data target. Derive a context + // from the explicit Frame chain stored in the Thread (sufficient for managed + // debugging stack walks). Walk the Frame chain and pick the first + // frame that yields a usable context: + // * If it is an InterpreterFrame, fill the context from the top + // InterpMethodContextFrame. + // * Otherwise, update the context from the frame and accept it when both + // StackPointer and InstructionPointer are non-zero. Mark the resulting + // context flags as full so callers know SP/PC/FP are valid. + // If no frame supplies a usable context (thread is not running managed code), + // return a zeroed context. } ``` 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 f1dbe19e0db53d..a07582f18c8698 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 @@ -326,11 +326,49 @@ byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource conte return bytes; } - if (!_target.TryGetThreadContext(thread.OSId.Value, contextFlags, buffer)) + if (_target.TryGetThreadContext(thread.OSId.Value, contextFlags, buffer)) { - throw new InvalidOperationException($"GetThreadContext failed for thread {thread.OSId.Value}"); + return bytes; + } + + // The data target does not implement GetThreadContext. Fall back to deriving + // a context from the explicit Frame chain stored in the Thread object. + return GetContextFromFrames(threadPointer); + } + + private byte[] GetContextFromFrames(TargetPointer threadPointer) + { + IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(_target); + + ThreadData threadData = ((IThread)this).GetThreadData(threadPointer); + FrameIterator iterator = new FrameIterator(_target, threadData); + while (iterator.IsValid()) + { + // For InterpreterFrame, fill the context from the top InterpMethodContextFrame + // (matches native InterpreterFrame::SetContextToInterpMethodContextFrame). + if (iterator.GetCurrentFrameType() == FrameType.InterpreterFrame) + { + context.Clear(); + iterator.UpdateContextFromCurrentFrame(context); + return context.GetBytes(); + } + + // For other frames, look for the first (deepest) frame that yields a context + // with both SP and PC set (e.g. RedirectedThreadFrame, InlinedCallFrame, + // DynamicHelperFrame). + context.Clear(); + iterator.UpdateContextFromCurrentFrame(context); + if (context.StackPointer.Value != 0 && context.InstructionPointer.Value != 0) + { + context.RawContextFlags = context.FullContextFlags; + return context.GetBytes(); + } + + iterator.Next(); } - return bytes; + // The thread is not running managed code: return a zeroed context. + context.Clear(); + return context.GetBytes(); } } From c567b805be7a08915f7922a05b5642f3e2d6e4f1 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Fri, 22 May 2026 23:53:07 -0700 Subject: [PATCH 2/3] Update Thread.md --- docs/design/datacontracts/Thread.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/design/datacontracts/Thread.md b/docs/design/datacontracts/Thread.md index 12b1eb83cf799d..b781ebfc7ea77e 100644 --- a/docs/design/datacontracts/Thread.md +++ b/docs/design/datacontracts/Thread.md @@ -408,8 +408,7 @@ byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource conte if (target.TryGetThreadContext(osId, contextFlags, bytes)) return bytes; - // GetThreadContext is not implemented on this data target. Derive a context - // from the explicit Frame chain stored in the Thread (sufficient for managed + // Derive a context from the explicit Frame chain stored in the Thread (sufficient for managed // debugging stack walks). Walk the Frame chain and pick the first // frame that yields a usable context: // * If it is an InterpreterFrame, fill the context from the top From 32e9de90947453ce65a91a40fabad4edaf31b81a Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Fri, 22 May 2026 23:54:02 -0700 Subject: [PATCH 3/3] Update Thread_1.cs --- .../Contracts/Thread_1.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 a07582f18c8698..b07eefcb31dfd7 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 @@ -331,8 +331,7 @@ byte[] IThread.GetContext(TargetPointer threadPointer, ThreadContextSource conte return bytes; } - // The data target does not implement GetThreadContext. Fall back to deriving - // a context from the explicit Frame chain stored in the Thread object. + // Fall back to deriving a context from the explicit Frame chain stored in the Thread object. return GetContextFromFrames(threadPointer); }