Skip to content
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] Implement basic stackwalking #111759

Open
wants to merge 34 commits into
base: main
Choose a base branch
from

Conversation

max-charlamb
Copy link
Contributor

@max-charlamb max-charlamb commented Jan 23, 2025

Contributes to #110758

Implements bulk of the cDAC stackwalking mechanics.

  • IStackWalk contract

Supports amd64 and arm64 architectures. Tested on Windows targeting win-amd64 and linux-arm64 dumps.

Supports InlineCallFrames and SoftwareExceptionFrames.

Does not support (will be added in future PRs:

  • Filtering based on CLRDataSimpleFrameType
  • Fetching Frame names.
  • IXCLRDataStackWalk::Request parameters CLRDATA_REQUEST_REVISION and CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME
  • ClrDataAccess::GetFrameName, currently falls back to the DAC implementation. Used by SOS to get the name of a given Frame.

@max-charlamb max-charlamb marked this pull request as ready for review February 6, 2025 23:22
#elif defined(FEATURE_CDAC_UNWINDER)

// TODO: add asserts on cDAC build without importing more headers.
#define UNWINDER_ASSERT(x)
Copy link
Contributor Author

@max-charlamb max-charlamb Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to add an assert here without importing common.h headers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a callback that calls the managed Debug.Fail() in the case where the assert fails? This would allow you to have a centralized handling of asserts in the cDAC (turn them into logging, etc.)

@max-charlamb max-charlamb changed the title [cDAC] Stackwalking [cDAC] Implement basic stackwalking Feb 6, 2025
@@ -367,7 +367,7 @@ extends:
- windows_x64
jobParameters:
templatePath: 'templates-official'
buildArgs: -s tools+libs -pack -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true
buildArgs: -s tools.illink+libs -pack -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @jkoritzinsky , cDAC should not currently be built in the Libraries_WithPackages run. This started failing because the native cDAC components were not available.

Comment on lines +13 to +18
// Creates a stack walk and returns a handle
IStackWalkHandle CreateStackWalk(ThreadData threadData);
// Iterates the stackWalkHandle to the next frame. If successful, returns true. Otherwise false.
bool Next(IStackWalkHandle stackWalkHandle);
// Gets the current frame from a stack walk and returns a IStackDataFrameHandle to it.
IStackDataFrameHandle GetCurrentFrame(IStackWalkHandle stackWalkHandle);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining a handle interface to drive enumeration and nothing else, could we instead provide the following method?

IEnumerable<IStackDataFrameHandle> EnumerateFrames(ThreadData threadData);

This method could lazily walk the frames (like how I implemented reading stress log messages in my StressLog cdac PR), which would preserve existing performance characteristics.

Copy link
Member

@davidwrighton davidwrighton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good. I'd like to see much more documentation in the markdown, and some fairly small tweaks here and there.

IStackWalkHandle CreateStackWalk(ThreadData threadData);
```

Creates a stack walk handle for the given thread data. This initializes the context and frame iterator for the stack walk.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see documentation about how to do all of this. We need that before we can actually merge this change, not just C# implementation in the actual cDAC codebase. Where we use the a well documented standard like the Win32 unwinder, you can just drop in a link, but for everything else... it needs correct documentation/algorithms

}
public unsafe byte[] GetBytes()
{
Span<T> structSpan = MemoryMarshal.CreateSpan(ref Context, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be new Span(ref Context) .... I also think you should probably make T have the unmanaged constraint.


namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;

public class ContnextHolder<T> : IPlatformAgnosticContext where T : struct, IPlatformContext
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContextHolder ... not ContnextHolder

@@ -31,6 +31,14 @@
<!-- <InstallRuntimeComponentDestination Include="sharedFramework" Condition="'$(RuntimeFlavor)' == 'coreclr'"/> -->
</ItemGroup>

<ItemGroup>
<!-- TODO: Link libraries on non-windows platforms. -->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should either be done as part of this PR, or you should have an open issue about this.

Copy link
Contributor Author

@max-charlamb max-charlamb Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is non-trivial, and I think best left another PR.
Created #112416 and linking it in the comment.

DataType.InlinedCallFrame,
DataType.SoftwareExceptionFrame,

DataType.ResumableFrame,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A giant list here, doesn't feel like the right implementation. This will change with different versions of the runtime, and in general we try to avoid putting business logic into the Data portion of the contracts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move this logic to the FrameIterator so it won't be on the IData type.

I was thinking about a better way to do this, but I'm not sure there is a great way because we need a list of DataTypes which inherit from Frame.

I was also considering moving the VPtr (soon to be enum values) from global in the datadescriptors to fields on the DataType. That isn't quite right either, because it isn't an offset, but an actual value. Maybe datadescriptors could support the notion of a "static" member in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we move to enum values... we can likely port the HasValidFrameIdentifier api, which is actually much simpler than this, since it can operate with a low and a high value for valid frame identifiers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants