-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[cDAC] Stack walk support more Frame types #112997
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Overview
This PR extends cDAC stack walking support by introducing new frame types and platform‐specific logic for AMD64 and ARM64. Key changes include new implementations for frames such as FramedMethodFrame, FaultingExceptionFrame, FuncEvalFrame, and HijackFrame; updated platform handlers (ARM64 and AMD64); and corresponding updates to DataType, context handling, and documentation.
Reviewed Changes
File | Description |
---|---|
Data/Frames/FramedMethodFrame.cs | Implements frame creation for FramedMethodFrame. |
Data/Frames/FaultingExceptionFrame.cs | Adds FaultingExceptionFrame with conditional context pointer initialization. |
Data/Frames/DebuggerEval.cs | Introduces DebuggerEval frame support. |
Data/Frames/FuncEvalFrame.cs | Implements FuncEvalFrame for function evaluation. |
Contracts/StackWalk/FrameHandling/IPlatformFrameHandler.cs | Declares interface for platform-specific frame handling methods. |
Data/Frames/CalleeSavedRegisters.cs | Reads callee-saved registers into a dictionary. |
Contracts/StackWalk/FrameHandling/FrameIterator.cs | Introduces frame iteration and type resolution logic. |
Contracts/StackWalk/FrameHandling/ARM64FrameHandler.cs | Provides ARM64-specific frame handling logic. |
Contracts/StackWalk/FrameHandling/AMD64FrameHandler.cs | Provides AMD64-specific frame handling logic. |
Contracts/StackWalk/Context/ContextHolder.cs | Renames and updates context holder implementation. |
Contracts/StackWalk/Context/ARM64Context.cs, AMD64Context.cs | Updates context structures with ToString overrides and public visibility. |
docs/design/datacontracts/StackWalk.md | Updates documentation to reflect new frame and data contract definitions. |
Abstractions/DataType.cs | Adds new DataType enum members for various frame types. |
Contracts/StackWalk/Context/IPlatformAgnosticContext.cs | Updates context creation to use the new ContextHolder. |
Contracts/StackWalk/FrameIterator.cs (removed) | Removes deprecated frame iterator implementation. |
Copilot reviewed 27 out of 27 changed files in this pull request and generated 1 comment.
...gnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/ARM64FrameHandler.cs
Show resolved
Hide resolved
|
||
Global variables used: | ||
| Global Name | Type | Purpose | | ||
| --- | --- | --- | | ||
| For each FrameType `<frameType>`, `<frameType>##Identifier` | FrameIdentifier enum value | Identifier used to determine concrete type of Frames | | ||
| For each FrameType `<frameType>`, `<frameType>##Identifier` | `FrameIdentifier` enum value | Identifier used to determine concrete type of Frames | | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bunch of architecture specific goo that isn't just data reading in the files like AMD64FrameHandler.cs and ARM64FrameHandler.cs. That all needs to be documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added docs on each type of Frame Context updating mechanism and their platform specific details.
value = default; | ||
return false; | ||
} | ||
value = new((ulong)field.GetValue(Context)!); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it should use Unsafe.As to basically bitcast the boxed field value to an nuint instead of doing a managed cast. You'd need to make sure you're not doing an unsafe overread first though, which I think is like... if IsPrimitive check sizeof(X) >= sizeof(TargetNUInt)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's also convention for a managed TryXXX method to not throw on failure, I think? So in this case if the field's value were not convertible to ulong, we'd throw instead of returning false. I'm not sure if that's desirable for diagnostics scenarios, presumably we already have exception handlers for the environment where this is running...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. This should not throw, we can let the caller decide to ignore or handle errors.
I reworked this to be less generic and only work for ulong
and uint
, the nuint
values we care about on the target platform.
I'd appreciate if you could take another look for safety. I want to be particularly careful around this use of reflection.
...rosoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ContextHolder.cs
Outdated
Show resolved
Hide resolved
...gnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/ARM64FrameHandler.cs
Outdated
Show resolved
Hide resolved
UpdateCalleeSavedRegistersFromOtherContext(otherContextHolder); | ||
|
||
_holder.InstructionPointer = otherContextHolder.Context.InstructionPointer; | ||
_holder.StackPointer = otherContextHolder.Context.StackPointer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FramePointer not being updated here caught my attention; is that because a 'software exception frame' is exception handling occurring within an outer frame and the frame pointer is shared? Do we want a comment? I can imagine this being obvious to someone who knows more about this area
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why the SEF does not update the FP. I created these handlers based off the runtime code and verified that the output is the same as the DAC.
For now, I added docs explaining what each Frame does to update the context, but I'd like to leave "why" out of scope for now.
Builds on #111759
Contributes to #110758
Adds cDAC stack walking support for the following Frame types:
TransitionFrame
(Base class)FramedMethodFrame
CLRToCOMMethodFrame
PInvokeCalliFrame
StubDispatchFrame
CallCountingHelperFrame
ExternalMethodFrame
DynamicHelperFrame
FuncEvalFrame
ResumableFrame
(Base class)RedirectedThreadFrame
FaultingExceptionFrame
HijackFrame
Expands cDAC Frame handling to be platform specific
In line with current cDAC stack walking support, only AMD64 and ARM64 support was added. When bringing up support for other architectures, platform specific frame handlers will be required. This was done to exactly match the existing DAC stack walking behavior.
Callouts
FaulingExceptionFrame
's stores aT_CONTEXT
directly.ResumableFrame
's store a pointer to aT_CONTEXT
(PT_CONTEXT
). To make this difference clear I named the direct struct asTargetContext
and the pointer asTargetContextPtr
. This distinction is important when reading the data descriptors. The former is a direct offset from the Frame's address, the latter is a pointer read at an offset from the Frame's address.TargetContext
andTargetContextPtr
) end up pointing to the beginning of aT_CONTEXT
.CalleeSavedRegisters
andHijackArgs
) store a bag of registers values. To prevent hard coding the number/names of these register value, I have opted to use reflection to write the corresponding struct values from their string names.IPlatformContext
to avoid reflection.CalleeSavedRegisters
/HijackArgs
<arch>datadescriptor.h
that is imported based on platform.Testing
Tested using amd64 Windows. Verified byte for byte correctness with DAC stack walking.
How produce call stacks with uncommon Frames
FuncEvalFrame
FuncEvalFrame's are generated during debugger immediate execution.
Debug any program in VS. Break and call a blocking function (
Console.ReadLine()
) attach a debugger non-invasively to stack walk or create a dump.For example, using cdb,
cdb -pv -p <processId>
HijackFrame
RedirectedThreadFrame (ResumableFrame)
FaultingExceptionFrame