Skip to content

Commit c19d421

Browse files
Max CharlambCopilot
andcommitted
[cDAC] Set first-arg register to InterpreterFrame in per-arch InlinedCallFrame handlers
Mirror the per-architecture native InlinedCallFrame::UpdateRegDisplay_Impl logic in the corresponding cDAC frame handlers: - AMD64FrameHandler.HandleInlinedCallFrame: sets Rcx (Windows) or Rdi (Unix SysV) to the InterpreterFrame address when the next Frame is an InterpreterFrame, matching src/coreclr/vm/amd64/cgenamd64.cpp:212-218. - ARM64FrameHandler.HandleInlinedCallFrame: sets X0 to the InterpreterFrame address when the next Frame is an InterpreterFrame, matching src/coreclr/vm/arm64/stubs.cpp:408-414. ARM, x86, LoongArch64, and RISCV64 native InlinedCallFrame::UpdateRegDisplay_Impl do NOT perform this update, so the corresponding cDAC handlers (or BaseFrameHandler inheritance) do not either. Two protected helpers are added to BaseFrameHandler so each handler can inspect the chain itself without changing the IPlatformFrameHandler interface: - GetNextFrame(currentFrameAddress): returns the next Data.Frame in the chain or null if the chain ends (handles the FRAME_TOP all-ones terminator). - IsInterpreterFrame(frame): compares the frame.Identifier against the runtime's InterpreterFrameIdentifier global. Without this update, cDAC reports the thread's literal saved Rcx for frames between an InlinedCallFrame and its successor InterpreterFrame, while the legacy DAC reports the InterpreterFrame address. This trips Debug.Assert(contextStruct.Equals(localContextStruct)) in ClrDataStackWalk.GetContext during !ClrStack on a thread captured during a P/Invoke from interpreted code. Verified locally against the SOS.InterpreterStackTest.Heap.dmp captured in CI: !ClrStack and !PrintException both succeed with the cDAC parity check enabled, walking through [InlinedCallFrame], JIT IL stub frames, [InterpreterFrame: ...], and the interpreted method frames in correct order. All 2081 cDAC unit tests continue to pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b244f33 commit c19d421

3 files changed

Lines changed: 67 additions & 0 deletions

File tree

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/AMD64FrameHandler.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,26 @@ public override void HandleFaultingExceptionFrame(FaultingExceptionFrame frame)
4040
// that does not support holding any extended state.
4141
_holder.Context.ContextFlags &= ~(uint)(ContextFlagsValues.CONTEXT_XSTATE & ContextFlagsValues.CONTEXT_AREA_MASK);
4242
}
43+
44+
public override void HandleInlinedCallFrame(InlinedCallFrame inlinedCallFrame)
45+
{
46+
base.HandleInlinedCallFrame(inlinedCallFrame);
47+
48+
// Mirror native InlinedCallFrame::UpdateRegDisplay_Impl in
49+
// src/coreclr/vm/amd64/cgenamd64.cpp:212-218: when the next Frame is
50+
// an InterpreterFrame, the first-argument register (Rcx on Win64,
51+
// Rdi on SysV/Unix AMD64) holds a pointer to that InterpreterFrame.
52+
Data.Frame? next = GetNextFrame(inlinedCallFrame.Address);
53+
if (next is not null && IsInterpreterFrame(next))
54+
{
55+
if (_target.Contracts.RuntimeInfo.GetTargetOperatingSystem() == RuntimeInfoOperatingSystem.Windows)
56+
{
57+
_holder.Context.Rcx = next.Address.Value;
58+
}
59+
else
60+
{
61+
_holder.Context.Rdi = next.Address.Value;
62+
}
63+
}
64+
}
4365
}

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/ARM64FrameHandler.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,19 @@ public override void HandleFaultingExceptionFrame(FaultingExceptionFrame frame)
3737
// that does not support holding any extended state.
3838
_holder.Context.ContextFlags &= ~(uint)(ContextFlagsValues.CONTEXT_XSTATE & ContextFlagsValues.CONTEXT_AREA_MASK);
3939
}
40+
41+
public override void HandleInlinedCallFrame(InlinedCallFrame inlinedCallFrame)
42+
{
43+
base.HandleInlinedCallFrame(inlinedCallFrame);
44+
45+
// Mirror native InlinedCallFrame::UpdateRegDisplay_Impl in
46+
// src/coreclr/vm/arm64/stubs.cpp:408-414: when the next Frame is an
47+
// InterpreterFrame, the first-argument register (X0) holds a pointer
48+
// to that InterpreterFrame.
49+
Data.Frame? next = GetNextFrame(inlinedCallFrame.Address);
50+
if (next is not null && IsInterpreterFrame(next))
51+
{
52+
_holder.Context.X0 = next.Address.Value;
53+
}
54+
}
4055
}

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,34 @@ protected void UpdateCalleeSavedRegistersFromOtherContext(IPlatformAgnosticConte
107107
}
108108
}
109109
}
110+
111+
/// <summary>
112+
/// Returns the next <see cref="Data.Frame"/> in the Frame chain after
113+
/// the frame at <paramref name="currentFrameAddress"/>, or <c>null</c>
114+
/// when the chain ends (the runtime stores <c>FRAME_TOP</c>, an all-ones
115+
/// pointer, as the terminator).
116+
/// </summary>
117+
protected Data.Frame? GetNextFrame(TargetPointer currentFrameAddress)
118+
{
119+
Data.Frame current = _target.ProcessedData.GetOrAdd<Data.Frame>(currentFrameAddress);
120+
if (current.Next == TargetPointer.Null)
121+
return null;
122+
ulong terminator = _target.PointerSize == 8 ? ulong.MaxValue : uint.MaxValue;
123+
if (current.Next.Value == terminator)
124+
return null;
125+
return _target.ProcessedData.GetOrAdd<Data.Frame>(current.Next);
126+
}
127+
128+
/// <summary>
129+
/// Returns true when <paramref name="frame"/>'s identifier matches the
130+
/// runtime's <c>InterpreterFrameIdentifier</c> global.
131+
/// </summary>
132+
protected bool IsInterpreterFrame(Data.Frame frame)
133+
{
134+
if (_target.TryReadGlobalPointer(nameof(StackWalkHelpers.FrameType.InterpreterFrame) + "Identifier", out TargetPointer? id))
135+
{
136+
return frame.Identifier == id;
137+
}
138+
return false;
139+
}
110140
}

0 commit comments

Comments
 (0)