diff --git a/docs/design/datacontracts/StackWalk.md b/docs/design/datacontracts/StackWalk.md index 080c3a7795e04c..50aec1621d11b5 100644 --- a/docs/design/datacontracts/StackWalk.md +++ b/docs/design/datacontracts/StackWalk.md @@ -58,6 +58,8 @@ This contract depends on the following descriptors: | `HijackArgs` (amd64 Windows) | `Rsp` | Saved stack pointer | | `HijackArgs` (arm64) | For each register `r` saved in HijackArgs, `r` | Register names associated with stored register values | | `CalleeSavedRegisters` | For each callee saved register `r`, `r` | Register names associated with stored register values | +| `TailCallFrame` (x86 Windows) | `CalleeSavedRegisters` | CalleeSavedRegisters data structure | +| `TailCallFrame` (x86 Windows) | `ReturnAddress` | Frame's stored instruction pointer | Global variables used: | Global Name | Type | Purpose | @@ -291,7 +293,7 @@ HijackFrames carry a IP (ReturnAddress) and a pointer to `HijackArgs`. All platf #### TailCallFrame -TailCallFrames are only used on Windows x86 which is not yet supported in the cDAC and therefore not implemented. +TailCallFrames only appear on x86 Windows. They hold a `CalleeSavedRegisters` struct as well as a `ReturnAddress`. While the stack pointer is not directly contained in the TailCallFrame structure, it will be on the stack immediately following the Frame (found at the address of the Frame + size of the Frame). To process these Frames, update all of the registers in `CalleeSavedRegisters`, the instruction pointer from the stored return address, and the stack pointer from the address saved on the stack. ### APIs diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 60193392bd9732..3c06d79f5f1e73 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -788,6 +788,14 @@ CDAC_TYPE_FIELD(FaultingExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data< #endif // FEATURE_EH_FUNCLETS CDAC_TYPE_END(FaultingExceptionFrame) +#if defined(TARGET_X86) && !defined(UNIX_X86_ABI) +CDAC_TYPE_BEGIN(TailCallFrame) +CDAC_TYPE_SIZE(sizeof(TailCallFrame)) +CDAC_TYPE_FIELD(TailCallFrame, /*CalleeSavedRegisters*/, CalleeSavedRegisters, cdac_data::CalleeSavedRegisters) +CDAC_TYPE_FIELD(TailCallFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) +CDAC_TYPE_END(TailCallFrame) +#endif // TARGET_X86 && !UNIX_X86_ABI + // CalleeSavedRegisters struct is different on each platform CDAC_TYPE_BEGIN(CalleeSavedRegisters) CDAC_TYPE_SIZE(sizeof(CalleeSavedRegisters)) diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index d01ae634fd5be2..892e55586d7abd 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2343,7 +2343,17 @@ class TailCallFrame : public Frame } void UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats = false); + + friend struct cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t CalleeSavedRegisters = offsetof(TailCallFrame, m_regs); + static constexpr size_t ReturnAddress = offsetof(TailCallFrame, m_ReturnAddress); }; + #endif // TARGET_X86 && !UNIX_X86_ABI //------------------------------------------------------------------------ diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index cda5f7de597926..ebb216265eb705 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -114,4 +114,5 @@ public enum DataType ResumableFrame, FaultingExceptionFrame, HijackFrame, + TailCallFrame, } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs index 31e912bdc5fa09..7fbcec81c9cd31 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs @@ -72,6 +72,11 @@ public virtual void HandleResumableFrame(ResumableFrame frame) _context.ReadFromAddress(_target, frame.TargetContextPtr); } + public virtual void HandleTailCallFrame(TailCallFrame tailCallFrame) + { + throw new InvalidOperationException("TailCallFrame handling is not implemented on the target platform."); + } + protected void UpdateFromRegisterDict(IReadOnlyDictionary registers) { foreach ((string name, TargetNUInt value) in registers) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs index 20b1faba88c531..4d482922bc72da 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs @@ -34,11 +34,12 @@ internal enum FrameType HijackFrame, + TailCallFrame, + /* Other Frame Types not handled by the iterator */ UnmanagedToManagedFrame, ComMethodFrame, ComPrestubMethodFrame, - TailCallFrame, ProtectValueClassFrame, DebuggerClassInitMarkFrame, DebuggerExitFrame, @@ -124,6 +125,10 @@ public void UpdateContextFromFrame(IPlatformAgnosticContext context) Data.HijackFrame hijackFrame = target.ProcessedData.GetOrAdd(CurrentFrame.Address); GetFrameHandler(context).HandleHijackFrame(hijackFrame); return; + case FrameType.TailCallFrame: + Data.TailCallFrame tailCallFrame = target.ProcessedData.GetOrAdd(CurrentFrame.Address); + GetFrameHandler(context).HandleTailCallFrame(tailCallFrame); + return; default: // Unknown Frame type. This could either be a Frame that we don't know how to handle, // or a Frame that does not update the context. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/IPlatformFrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/IPlatformFrameHandler.cs index 28e40d281763d2..30949b6f1e48bd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/IPlatformFrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/IPlatformFrameHandler.cs @@ -18,4 +18,5 @@ internal interface IPlatformFrameHandler void HandleResumableFrame(Data.ResumableFrame frame); void HandleFaultingExceptionFrame(Data.FaultingExceptionFrame frame); void HandleHijackFrame(Data.HijackFrame frame); + void HandleTailCallFrame(Data.TailCallFrame frame); } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs index b179c923e5ba1b..4f1e9d2a2a2480 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs @@ -31,6 +31,21 @@ public void HandleHijackFrame(HijackFrame frame) throw new NotImplementedException(); } + public override void HandleTailCallFrame(TailCallFrame frame) + { + _context.Context.Eip = (uint)frame.ReturnAddress; + + // The stack pointer is set to the address immediately after the TailCallFrame structure. + if (_target.GetTypeInfo(DataType.TailCallFrame).Size is not uint tailCallFrameSize) + { + throw new InvalidOperationException("TailCallFrame missing size information"); + } + _context.Context.Esp = (uint)(frame.Address + tailCallFrameSize); + + CalleeSavedRegisters calleeSavedRegisters = _target.ProcessedData.GetOrAdd(frame.CalleeSavedRegisters); + UpdateFromRegisterDict(calleeSavedRegisters.Registers); + } + public override void HandleFuncEvalFrame(FuncEvalFrame funcEvalFrame) { Data.DebuggerEval debuggerEval = _target.ProcessedData.GetOrAdd(funcEvalFrame.DebuggerEvalPtr); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/TailCallFrame.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/TailCallFrame.cs new file mode 100644 index 00000000000000..41381ff5fe88c9 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/TailCallFrame.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal class TailCallFrame : IData +{ + static TailCallFrame IData.Create(Target target, TargetPointer address) + => new TailCallFrame(target, address); + + public TailCallFrame(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.TailCallFrame); + Address = address; + CalleeSavedRegisters = address + (ulong)type.Fields[nameof(CalleeSavedRegisters)].Offset; + ReturnAddress = target.ReadPointer(address + (ulong)type.Fields[nameof(ReturnAddress)].Offset); + } + + public TargetPointer Address { get; } + public TargetPointer CalleeSavedRegisters { get; } + public TargetPointer ReturnAddress { get; } +}