Skip to content

Commit

Permalink
remove GCCover contract and move API to CodeVersions
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Charlamb committed Dec 10, 2024
1 parent 22ed710 commit 4fc1b6f
Show file tree
Hide file tree
Showing 22 changed files with 146 additions and 260 deletions.
13 changes: 13 additions & 0 deletions docs/design/datacontracts/CodeVersions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
### Extension Methods
```csharp
Expand All @@ -61,13 +64,15 @@ Data descriptors used:
| NativeCodeVersionNode | NativeCode | indicates an explicit native code version node |
| NativeCodeVersionNode | Flags | `NativeCodeVersionNodeFlags` flags, see below |
| NativeCodeVersionNode | VersionId | Version ID corresponding to the parent IL code version |
| NativeCodeVersionNode | GCCoverageInfo | GCStress debug info, if supported |
| ILCodeVersioningState | FirstVersionNode | pointer to the first `ILCodeVersionNode` |
| ILCodeVersioningState | ActiveVersionKind | an `ILCodeVersionKind` value indicating which fields of the active version are value |
| ILCodeVersioningState | ActiveVersionNode | if the active version is explicit, the NativeCodeVersionNode for the active version |
| ILCodeVersioningState | ActiveVersionModule | if the active version is synthetic or unknown, the pointer to the Module that defines the method |
| ILCodeVersioningState | ActiveVersionMethodDef | if the active version is synthetic or unknown, the MethodDef token for the method |
| ILCodeVersionNode | VersionId | Version ID of the node |
| ILCodeVersionNode | Next | Pointer to the next `ILCodeVersionNode`|
| GCCoverageInfo | SavedCode | Pointer to the GCCover saved code copy |

The flag indicates that the default version of the code for a method desc is active:
```csharp
Expand Down Expand Up @@ -249,3 +254,11 @@ bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddr
return true;
}
```

### Finding GCStress Code Copy
```csharp
public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle);
```

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.
6 changes: 4 additions & 2 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ partial interface IRuntimeTypeSystem : IContract
// Get an instruction pointer that can be called to cause the MethodDesc to be executed
public virtual TargetCodePointer GetNativeCode(MethodDescHandle methodDesc);

// Gets the GCCoverageInfo pointer if available, otherwise returns TargetPointer.Null
public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc);
// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc);
}
```

Expand Down Expand Up @@ -640,6 +640,7 @@ We depend on the following data descriptors:
| `MethodDesc` | `Slot` | The method's slot |
| `MethodDesc` | `Flags` | The method's flags |
| `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID |
| `MethodDesc` | `GCCoverageInfo` | The method's GCCover debug info, if supported |
| `MethodDescCodeData` | `VersioningState` | The IL versioning state associated with a method descriptor
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to |
| `MethodDescChunk` | `Next` | The next chunk of methods |
Expand All @@ -656,6 +657,7 @@ We depend on the following data descriptors:
| `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature |
| `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` |
| `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc |
| `GCCoverageInfo` | `SavedCode` | Pointer to the GCCover saved code copy |


The contract depends on the following other contracts
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/debug/runtimeinfo/contracts.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"EcmaMetadata" : 1,
"Exception": 1,
"ExecutionManager": 2,
"GCCover": 1,
"Loader": 1,
"Object": 1,
"PlatformMetadata": 1,
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data<Metho
CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, EntryPointFlags, cdac_data<MethodDesc>::EntryPointFlags)
CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data<MethodDesc>::CodeData)
#ifdef HAVE_GCCOVER
CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, cdac_data<MethodDesc>::GCCoverageInfo)
CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, MethodDesc::m_GcCover)
#endif // HAVE_GCCOVER
CDAC_TYPE_END(MethodDesc)

Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1910,9 +1910,6 @@ template<> struct cdac_data<MethodDesc>
static constexpr size_t Flags3AndTokenRemainder = offsetof(MethodDesc, m_wFlags3AndTokenRemainder);
static constexpr size_t EntryPointFlags = offsetof(MethodDesc, m_bFlags4);
static constexpr size_t CodeData = offsetof(MethodDesc, m_codeData);
#ifdef HAVE_GCCOVER
static constexpr size_t GCCoverageInfo = offsetof(MethodDesc, m_GcCover);
#endif // HAVE_GCCOVER
};

#ifndef DACCESS_COMPILE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,4 @@ internal abstract class ContractRegistry
/// Gets an instance of the ReJIT contract for the target.
/// </summary>
public abstract IReJIT ReJIT { get; }
/// <summary>
/// Gets an instance of the GCCover contract for the target.
/// </summary>
public abstract IGCCover GCCover { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal interface ICodeVersions : IContract

public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException();

public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle) => throw new NotImplementedException();

public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc) => throw new NotImplementedException();
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ internal interface IRuntimeTypeSystem : IContract

public virtual TargetPointer GetAddressOfNativeCodeSlot(MethodDescHandle methodDesc) => throw new NotImplementedException();

public virtual TargetPointer GetGCCoverageInfo(MethodDescHandle methodDesc) => throw new NotImplementedException();
public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc) => throw new NotImplementedException();
#endregion MethodDesc inspection APIs
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,30 @@ NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion
return (ilVersionId == codeVersion.ILVersionId)
&& ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
});
}

TargetPointer ICodeVersions.GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle)
{
Debug.Assert(codeVersionHandle.Valid);

if (!codeVersionHandle.IsExplicit)
{
// NativeCodeVersion::GetGCCoverageInfo
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(codeVersionHandle.MethodDescAddress);
return rts.GetGCStressCodeCopy(md);
}
else
{
// NativeCodeVersionNode::GetGCCoverageInfo
NativeCodeVersionNode codeVersionNode = AsNode(codeVersionHandle);
if (codeVersionNode.GCCoverageInfo is TargetPointer gcCoverageInfoAddr)
{
Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo);
return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset;
}
return TargetPointer.Null;
}
}

[Flags]
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -1069,10 +1069,15 @@ private TargetCodePointer GetMethodEntryPointIfExists(TargetPointer methodDescAd
return _target.ReadCodePointer(addrOfSlot);
}

TargetPointer IRuntimeTypeSystem.GetGCCoverageInfo(MethodDescHandle methodDesc)
TargetPointer IRuntimeTypeSystem.GetGCStressCodeCopy(MethodDescHandle methodDesc)
{
MethodDesc md = _methodDescs[methodDesc.Address];
return md.GCCoverageInfo ?? TargetPointer.Null;
if (md.GCCoverageInfo is TargetPointer gcCoverageInfoAddr)
{
Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo);
return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset;
}
return TargetPointer.Null;
}

private class NonValidatedMethodTableQueries : MethodValidation.IMethodTableQueries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG
[typeof(IPlatformMetadata)] = new PlatformMetadataFactory(),
[typeof(IPrecodeStubs)] = new PrecodeStubsFactory(),
[typeof(IReJIT)] = new ReJITFactory(),
[typeof(IGCCover)] = new GCCoverFactory(),
};
configureFactories?.Invoke(_factories);
}
Expand All @@ -54,7 +53,6 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG
public override IPlatformMetadata PlatformMetadata => GetContract<IPlatformMetadata>();
public override IPrecodeStubs PrecodeStubs => GetContract<IPrecodeStubs>();
public override IReJIT ReJIT => GetContract<IReJIT>();
public override IGCCover GCCover => GetContract<IGCCover>();

private TContract GetContract<TContract>() where TContract : IContract
{
Expand Down
12 changes: 3 additions & 9 deletions src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,15 +352,9 @@ int ISOSDacInterface.GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDes
// HAVE_GCCOVER
if (requestedNativeCodeVersion.Valid)
{
IGCCover gcCover = _target.Contracts.GCCover;
TargetPointer gcCoverAddr = gcCover.GetGCCoverageInfo(requestedNativeCodeVersion);
if (gcCoverAddr != TargetPointer.Null)
{
// 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 + (ulong)gcCoverageInfo.Fields["SavedCode"].Offset;
}
// This is often TargetPointer.Null. 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).
data->GCInfo = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion);
}

if (data->bIsDynamic != 0)
Expand Down
79 changes: 79 additions & 0 deletions src/native/managed/cdacreader/tests/CodeVersionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,24 @@ internal static Target CreateTarget(
return target;
}

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;
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetNativeCodeVersion_Null(MockTarget.Architecture arch)
Expand Down Expand Up @@ -666,4 +684,65 @@ public void IlToNativeToIlCodeVersion_SyntheticAndExplicit(MockTarget.Architectu
NativeCodeVersionHandle syntheticNativeCodeVersion = codeVersions.GetActiveNativeCodeVersionForILCodeVersion(methodDescAddress, syntheticILcodeVersion);
Assert.True(syntheticILcodeVersion.Equals(codeVersions.GetILCodeVersion(syntheticNativeCodeVersion)));
}

private void GetGCStressCodeCopy_Impl(MockTarget.Architecture arch, bool returnsNull)
{
MockCodeVersions builder = new(arch);
Mock<IRuntimeTypeSystem> mockRTS = new();

// Setup synthetic NativeCodeVersion
TargetPointer expectedSyntheticCodeCopyAddr = returnsNull ? TargetPointer.Null : new(0x2345_6789);
TargetPointer syntheticMethodDescAddr = new(0x2345_8000);
NativeCodeVersionHandle syntheticHandle = NativeCodeVersionHandle.CreateSynthetic(syntheticMethodDescAddr);
MethodDescHandle methodDescHandle = new MethodDescHandle(syntheticMethodDescAddr);
mockRTS.Setup(rts => rts.GetMethodDescHandle(syntheticMethodDescAddr)).Returns(methodDescHandle);
mockRTS.Setup(rts => rts.GetGCStressCodeCopy(methodDescHandle)).Returns(expectedSyntheticCodeCopyAddr);

// Setup explicit NativeCodeVersion
TargetPointer? explicitGCCoverageInfoAddr = returnsNull ? TargetPointer.Null : new(0x1234_5678);
TargetPointer nativeCodeVersionNode = builder.AddNativeCodeVersionNode();
builder.FillNativeCodeVersionNode(
nativeCodeVersionNode,
methodDesc: TargetPointer.Null,
nativeCode: TargetCodePointer.Null,
next: TargetPointer.Null,
isActive: true,
ilVersionId: new(1),
gcCoverageInfo: explicitGCCoverageInfoAddr);
NativeCodeVersionHandle explicitHandle = NativeCodeVersionHandle.CreateExplicit(nativeCodeVersionNode);

var target = CreateTarget(arch, builder, mockRTS);
var codeVersions = target.Contracts.CodeVersions;

// TEST
TargetPointer actualSyntheticCodeCopyAddr = codeVersions.GetGCStressCodeCopy(syntheticHandle);
Assert.Equal(expectedSyntheticCodeCopyAddr, actualSyntheticCodeCopyAddr);

if(returnsNull)
{
TargetPointer actualExplicitCodeCopyAddr = codeVersions.GetGCStressCodeCopy(explicitHandle);
Assert.Equal(TargetPointer.Null, actualExplicitCodeCopyAddr);
}
else
{
Target.TypeInfo gcCoverageInfoType = target.GetTypeInfo(DataType.GCCoverageInfo);
TargetPointer expectedExplicitCodeCopyAddr = explicitGCCoverageInfoAddr.Value + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset;
TargetPointer actualExplicitCodeCopyAddr = codeVersions.GetGCStressCodeCopy(explicitHandle);
Assert.Equal(expectedExplicitCodeCopyAddr, actualExplicitCodeCopyAddr);
}
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetGCStressCodeCopy_Null(MockTarget.Architecture arch)
{
GetGCStressCodeCopy_Impl(arch, returnsNull: true);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetGCStressCodeCopy_NotNull(MockTarget.Architecture arch)
{
GetGCStressCodeCopy_Impl(arch, returnsNull: false);
}
}
Loading

0 comments on commit 4fc1b6f

Please sign in to comment.