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 GCCover portion of SOSDacImpl::GetMethodDescData #110322

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

Conversation

max-charlamb
Copy link
Contributor

@max-charlamb max-charlamb commented Dec 2, 2024

Contributes to #109426

Changes

  • Adds APIs needed to access the GCStressCodeCopy.
  • Modifies ICodeVersions contract
/* adds */ public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle);
  • Modifies IRuntimeTypeSystem contract
/* adds */ public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc);

Worklog

  • Add GCCoverageInfo fields
  • Main Implementation
  • Unit tests
  • Update docs
  • Manual Testing
    • To verify I attached WinDBG to a C# program with $env:DOTNET_GCStress=4. This seems to make normal breakpoints not work, but throws an exception on every inserted GC breakpoint. I configured WinDBG to break on these exceptions (sxe c0000096) and used !ip2md <ip> to query the md which contains gccoverage info.

// In certain minidumps, we won't save the gccover information.
// (it would be unwise to do so, it is heavy and not a customer scenario).
Target.TypeInfo gcCoverageInfo = _target.GetTypeInfo(DataType.GCCoverageInfo);
data->GCStressCodeCopy = gcCoverAddr.Value.Value + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset;
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 don't like having the inline string here, but I wasn't sure where to store this field.

{
IGCCover IContractFactory<IGCCover>.CreateContract(Target target, int version)
{
return version switch
Copy link
Member

Choose a reason for hiding this comment

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

I like how you went for the switch despite only having an if/else condition to have it ready for the likely new versions in the future 🙂

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 can't really take credit for this. Aleksey used this factory pattern in the other contracts a few months ago. I do agree its a good approach. 😄

Copy link
Member

@elinor-fung elinor-fung left a comment

Choose a reason for hiding this comment

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

  • Adds IGCCover contract to get access to gc coverage field from native code version.
    • While this isn't strictly necessary, the API could live on the CodeVersions contract, I thought it made more sense to keep them logically separated.

This seems to be in an in-between state now, since there is a GCCover contract that gives the separation from the CodeVersions contract, but the RuntimeTypeSystem now also deals with GC coverage info.

I think we should either:

  • Add a GCCover contract that handles getting GC coverage info from a native code version and from a method desc
    • I think this would be the logical separation that you mentioned
  • Add methods to the CodeVersions and RuntimeTypeSystem contracts for getting GC coverage info (no new GCCover contract)
    • Since we don't expect the question of 'is GC cover enabled' to be a scenario for a consumer, so it may make sense to have GC coverage info at a lower level instead of elevated to its own contract

src/coreclr/vm/gccover.h Outdated Show resolved Hide resolved
src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs Outdated Show resolved Hide resolved
src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs Outdated Show resolved Hide resolved
Comment on lines +257 to +273
internal static Target CreateTarget(
MockTarget.Architecture arch,
MockCodeVersions builder,
Mock<IRuntimeTypeSystem> mockRuntimeTypeSystem)
{
TestPlaceholderTarget target = new TestPlaceholderTarget(
arch,
builder.Builder.GetReadContext().ReadFromTarget,
builder.Types);

IContractFactory<ICodeVersions> cvfactory = new CodeVersionsFactory();
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.CodeVersions == cvfactory.CreateContract(target, 1)
&& c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object);
target.SetContracts(reg);
return target;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previous CodeVersion tests use custom "mock" classes. Now that we have Moq integrated, I used it to create the new GCStressCodeCopy tests.

Copy link
Member

Choose a reason for hiding this comment

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

Not for this change, but might be nice to switch the previous/existing tests to this as well.

@max-charlamb
Copy link
Contributor Author

  • Adds IGCCover contract to get access to gc coverage field from native code version.

    • While this isn't strictly necessary, the API could live on the CodeVersions contract, I thought it made more sense to keep them logically separated.

This seems to be in an in-between state now, since there is a GCCover contract that gives the separation from the CodeVersions contract, but the RuntimeTypeSystem now also deals with GC coverage info.

I think we should either:

  • Add a GCCover contract that handles getting GC coverage info from a native code version and from a method desc

    • I think this would be the logical separation that you mentioned
  • Add methods to the CodeVersions and RuntimeTypeSystem contracts for getting GC coverage info (no new GCCover contract)

    • Since we don't expect the question of 'is GC cover enabled' to be a scenario for a consumer, so it may make sense to have GC coverage info at a lower level instead of elevated to its own contract

I agree with this assessment. I ended up going with the second option, moving the API to ICodeVersions. I think this follows existing patterns which read either from a code version node or the method description depending on if the code version is synthetic or explicit.

I think having a separate GCCover contract which handles the method desc and code version node fetching would be confusing as it directly depends on two underlying areas which should be accessed through their respective contracts.

```

1. If `codeVersionHandle` is synthetic, use the `IRuntimeTypeSystem` to find the GCStressCodeCopy.
2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value is nullable as it only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value is nullable as it only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct.
2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct.

Comment on lines +257 to +273
internal static Target CreateTarget(
MockTarget.Architecture arch,
MockCodeVersions builder,
Mock<IRuntimeTypeSystem> mockRuntimeTypeSystem)
{
TestPlaceholderTarget target = new TestPlaceholderTarget(
arch,
builder.Builder.GetReadContext().ReadFromTarget,
builder.Types);

IContractFactory<ICodeVersions> cvfactory = new CodeVersionsFactory();
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.CodeVersions == cvfactory.CreateContract(target, 1)
&& c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object);
target.SetContracts(reg);
return target;
}
Copy link
Member

Choose a reason for hiding this comment

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

Not for this change, but might be nice to switch the previous/existing tests to this as well.

@@ -40,6 +40,9 @@ public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc);

// Return the instruction pointer corresponding to the start of the given native code version
public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle);

// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle);
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to refer to it as 'GCCoverage' or 'GCStress'? I kind of think of GC coverage as the more basic feature/info that GC stress uses. I don't feel too strongly either way.

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.

4 participants