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] Handle non-default active version and explicit IL code version cases for CodeVersions.GetActiveNativeCodeVersion #109560

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 37 additions & 15 deletions docs/design/datacontracts/CodeVersions.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ Data descriptors used:
| NativeCodeVersionNode | Next | pointer to the next native code version |
| NativeCodeVersionNode | MethodDesc | indicates a synthetic native code version node |
| 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 |
| 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 |

The flag indicates that the default version of the code for a method desc is active:
```csharp
Expand All @@ -63,6 +66,14 @@ internal enum MethodDescVersioningStateFlags : byte
};
```

The flag indicates the native code version is active:
```csharp
internal enum NativeCodeVersionNodeFlags : uint
{
IsActiveChild = 1
};
```

The value of the `ILCodeVersioningState::ActiveVersionKind` field is one of:
```csharp
private enum ILCodeVersionKind
Expand Down Expand Up @@ -120,21 +131,23 @@ NativeCodeVersionHandle GetSpecificNativeCodeVersion(MethodDescHandle md, Target
NativeCodeVersionHandle first = new NativeCodeVersionHandle(md.Address, TargetPointer.Null);
return first;
}
// ImplicitCodeVersion stage of NativeCodeVersionIterator::Next()
TargetPointer methodDescVersioningStateAddress = target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md);
if (methodDescVersioningStateAddress == TargetPointer.Null)
{
return NativeCodeVersionHandle.Invalid;
}
Data.MethodDescVersioningState methodDescVersioningStateData = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(methodDescVersioningStateAddress);
return FindFirstCodeVersion(methodDescVersioningStateData, (codeVersion) =>

return FindFirstCodeVersion(rts, md, (codeVersion) =>
{
return codeVersion.MethodDesc == md.Address && codeVersion.NativeCode == startAddress;
});
}

NativeCodeVersionHandle FindFirstCodeVersion(Data.MethodDescVersioningState versioningState, Func<Data.NativeCodeVersionNode, bool> predicate)
NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, MethodDescHandle md, Func<Data.NativeCodeVersionNode, bool> predicate)
{
// ImplicitCodeVersion stage of NativeCodeVersionIterator::Next()
TargetPointer versioningStateAddr = rts.GetMethodDescVersioningState(md);
if (versioningStateAddr == TargetPointer.Null)
return NativeCodeVersionHandle.Invalid;

Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddr);

// LinkedList stage of NativeCodeVersion::Next, heavily inlined
TargetPointer currentAddress = versioningState.NativeCodeVersionNode;
while (currentAddress != TargetPointer.Null)
{
Expand Down Expand Up @@ -211,7 +224,8 @@ bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion)
}
else if (nativeCodeVersion.CodeVersionNodeAddress != TargetPointer.Null)
{
throw new NotImplementedException(); // TODO[cdac]: IsActiveNativeCodeVersion - explicit
uint flags = _target.Read<uint>(nativeCodeVersion.CodeVersionNodeAddress + /* NativeCodVersionNode::Flags offset*/)
return ((NativeCodeVersionNodeFlags)flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
}
else
{
Expand All @@ -221,22 +235,30 @@ bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion)

NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress)
{
TargetNUInt? ilVersionId = default;
if (methodDefActiveVersion.Module != TargetPointer.Null)
{
NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null);
if (IsActiveNativeCodeVersion(provisionalHandle))
{
return provisionalHandle;
}
else
{
throw new NotImplementedException(); // TODO[cdac]: iterate through versioning state nodes
}
}
else
{
throw new NotImplementedException(); // TODO: [cdac] find explicit il code version
// Get the explicit IL code version
Debug.Assert(methodDefActiveVersion.ILCodeVersionNode != TargetPointer.Null);
ilVersionId = _target.ReadNUint(methodDefActiveVersion.ILCodeVersionNode + /* ILCodeVersionNode::VersionId offset */);
}

// Iterate through versioning state nodes and return the active one, matching any IL code version
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
return FindFirstCodeVersion(rts, md, (codeVersion) =>
{
return (!ilVersionId.HasValue || ilVersionId.Value.Value == codeVersion.ILVersionId.Value)
&& ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
});
}
```

Expand Down
9 changes: 7 additions & 2 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ The contract additionally depends on these data descriptors

### MethodDesc

The version 1 `MethodDesc` APIs depend on the `MethodDescAlignment` global and the `MethodDesc` and `MethodDescChunk` data descriptors.
The version 1 `MethodDesc` APIs depend on the following globals:

| Global name | Meaning |
| --- | --- |
Expand All @@ -644,7 +644,9 @@ We depend on the following data descriptors:
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment` |
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1. |
| `MethodDescChunk` | `FlagsAndTokenRange` | `MethodDescChunk` flags, and the upper bits of the method token's RID |
| `MethodTable` | `AuxiliaryData` | Auxiliary data associated with the method table. See `MethodTableAuxiliaryData` |
| `MethodTableAuxiliaryData` | `LoaderModule` | The loader module associated with a method table
| `MethodTableAuxiliaryData` | `OffsetToNonVirtualSlots` | Offset from the auxiliary data address to the array of non-virtual slots |
| `InstantiatedMethodDesc` | `PerInstInfo` | The pointer to the method's type arguments |
| `InstantiatedMethodDesc` | `Flags2` | Flags for the `InstantiatedMethodDesc` |
| `InstantiatedMethodDesc` | `NumGenericArgs` | How many generic args the method has |
Expand Down Expand Up @@ -1123,7 +1125,10 @@ Getting the native code pointer for methods with a NativeCodeSlot or a stable en
}
else
{
// TODO[cdac]: GetNonVirtualSlotsArray from MethodTableAuxiliaryData
// Non-virtual slots < GetNumVtableSlots live before the MethodTableAuxiliaryData. The array grows backwards
TargetPointer auxDataPtr = _target.ReadPointer(typeHandle.Address + /* MethodTable::AuxiliaryData offset */);
TargetPointer nonVirtualSlotsArray = auxDataPtr + _target.Read<short>(/* MethodTableAuxiliaryData::OffsetToNonVirtualSlots offset */);
return nonVirtualSlotsArray - (1 + (slotNum - mt.NumVirtuals));
}

}
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ CDAC_TYPE_END(MethodTable)
CDAC_TYPE_BEGIN(MethodTableAuxiliaryData)
CDAC_TYPE_INDETERMINATE(MethodTableAuxiliaryData)
CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*pointer*/, LoaderModule, offsetof(MethodTableAuxiliaryData, m_pLoaderModule))
CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*int16*/, OffsetToNonVirtualSlots, offsetof(MethodTableAuxiliaryData, m_offsetToNonVirtualSlots))
CDAC_TYPE_END(MethodTableAuxiliaryData)

CDAC_TYPE_BEGIN(EEClass)
Expand Down Expand Up @@ -461,8 +462,15 @@ CDAC_TYPE_INDETERMINATE(NativeCodeVersionNode)
CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, Next, cdac_data<NativeCodeVersionNode>::Next)
CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, MethodDesc, cdac_data<NativeCodeVersionNode>::MethodDesc)
CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, NativeCode, cdac_data<NativeCodeVersionNode>::NativeCode)
CDAC_TYPE_FIELD(NativeCodeVersionNode, /*uint32*/, Flags, cdac_data<NativeCodeVersionNode>::Flags)
CDAC_TYPE_FIELD(NativeCodeVersionNode, /*nuint*/, ILVersionId, cdac_data<NativeCodeVersionNode>::ILVersionId)
CDAC_TYPE_END(NativeCodeVersionNode)

CDAC_TYPE_BEGIN(ILCodeVersionNode)
CDAC_TYPE_INDETERMINATE(ILCodeVersionNode)
CDAC_TYPE_FIELD(ILCodeVersionNode, /*nuint*/, VersionId, cdac_data<ILCodeVersionNode>::VersionId)
CDAC_TYPE_END(ILCodeVersionNode)

CDAC_TYPE_BEGIN(ProfControlBlock)
CDAC_TYPE_FIELD(ProfControlBlock, /*uint64*/, GlobalEventMask, offsetof(ProfControlBlock, globalEventMask))
CDAC_TYPE_END(ProfControlBlock)
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/vm/codeversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ struct cdac_data<NativeCodeVersionNode>
static constexpr size_t Next = offsetof(NativeCodeVersionNode, m_pNextMethodDescSibling);
static constexpr size_t MethodDesc = offsetof(NativeCodeVersionNode, m_pMethodDesc);
static constexpr size_t NativeCode = offsetof(NativeCodeVersionNode, m_pNativeCode);
static constexpr size_t Flags = offsetof(NativeCodeVersionNode, m_flags);
static constexpr size_t ILVersionId = offsetof(NativeCodeVersionNode, m_parentId);
};

class NativeCodeVersionCollection
Expand Down Expand Up @@ -409,6 +411,14 @@ class ILCodeVersionNode
Volatile<DWORD> m_jitFlags;
InstrumentedILOffsetMapping m_instrumentedILMap;
BOOL m_deoptimized;

friend struct ::cdac_data<ILCodeVersionNode>;
};

template<>
struct cdac_data<ILCodeVersionNode>
{
static constexpr size_t VersionId = offsetof(ILCodeVersionNode, m_rejitId);
};

class ILCodeVersionCollection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ public enum DataType
ILCodeVersioningState,
NativeCodeVersionNode,
ProfControlBlock,
ILCodeVersionNode
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

Expand Down Expand Up @@ -106,18 +107,15 @@ internal struct ILCodeVersionHandle

internal ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress)
{
Module = module;
MethodDefinition = methodDef;
ILCodeVersionNode = ilCodeVersionNodeAddress;
if (Module != TargetPointer.Null && ILCodeVersionNode != TargetPointer.Null)
{
if (module != TargetPointer.Null && ilCodeVersionNodeAddress != TargetPointer.Null)
throw new ArgumentException("Both MethodDesc and ILCodeVersionNode cannot be non-null");

}
if (Module != TargetPointer.Null && MethodDefinition == 0)
{
if (module != TargetPointer.Null && methodDef == 0)
throw new ArgumentException("MethodDefinition must be non-zero if Module is non-null");
}

Module = module;
MethodDefinition = methodDef;
ILCodeVersionNode = ilCodeVersionNodeAddress;
}
public static ILCodeVersionHandle Invalid => new ILCodeVersionHandle(TargetPointer.Null, 0, TargetPointer.Null);
public bool IsValid => Module != TargetPointer.Null || ILCodeVersionNode != TargetPointer.Null;
Expand All @@ -129,7 +127,6 @@ internal enum MethodDescVersioningStateFlags : byte
IsDefaultVersionActiveChildFlag = 0x4
};


private NativeCodeVersionHandle GetSpecificNativeCodeVersion(IRuntimeTypeSystem rts, MethodDescHandle md, TargetCodePointer startAddress)
{
// initial stage of NativeCodeVersionIterator::Next() with a null m_ilCodeFilter
Expand All @@ -139,23 +136,24 @@ private NativeCodeVersionHandle GetSpecificNativeCodeVersion(IRuntimeTypeSystem
NativeCodeVersionHandle first = new NativeCodeVersionHandle(md.Address, TargetPointer.Null);
return first;
}
// ImplicitCodeVersion stage of NativeCodeVersionIterator::Next()
TargetPointer methodDescVersioningStateAddress = rts.GetMethodDescVersioningState(md);
if (methodDescVersioningStateAddress == TargetPointer.Null)
{
return NativeCodeVersionHandle.Invalid;
}
Data.MethodDescVersioningState methodDescVersioningStateData = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(methodDescVersioningStateAddress);

// CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc, PCODE startAddress)
return FindFirstCodeVersion(methodDescVersioningStateData, (codeVersion) =>
return FindFirstCodeVersion(rts, md, (codeVersion) =>
{
return codeVersion.MethodDesc == md.Address && codeVersion.NativeCode == startAddress;
});
}

private NativeCodeVersionHandle FindFirstCodeVersion(Data.MethodDescVersioningState versioningState, Func<Data.NativeCodeVersionNode, bool> predicate)
private NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, MethodDescHandle md, Func<Data.NativeCodeVersionNode, bool> predicate)
{
// NativeCodeVersion::Next, heavily inlined
// ImplicitCodeVersion stage of NativeCodeVersionIterator::Next()
TargetPointer versioningStateAddr = rts.GetMethodDescVersioningState(md);
if (versioningStateAddr == TargetPointer.Null)
return NativeCodeVersionHandle.Invalid;

Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddr);

// LinkedList stage of NativeCodeVersion::Next, heavily inlined
TargetPointer currentAddress = versioningState.NativeCodeVersionNode;
while (currentAddress != TargetPointer.Null)
{
Expand All @@ -169,7 +167,6 @@ private NativeCodeVersionHandle FindFirstCodeVersion(Data.MethodDescVersioningSt
return NativeCodeVersionHandle.Invalid;
}


private enum ILCodeVersionKind
{
Unknown = 0,
Expand All @@ -192,6 +189,7 @@ private static ILCodeVersionHandle ILCodeVersionHandleFromState(Data.ILCodeVersi

private ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint methodDefinition)
{
// CodeVersionManager::GetActiveILCodeVersion
ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module);
TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState;
TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefinition, out var _);
Expand All @@ -203,8 +201,15 @@ private ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint m
return ILCodeVersionHandleFromState(ilState);
}

[Flags]
internal enum NativeCodeVersionNodeFlags : uint
{
IsActiveChild = 1
};

private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion)
{
// NativeCodeVersion::IsActiveChildVersion
if (nativeCodeVersion.MethodDescAddress != TargetPointer.Null)
{
MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress);
Expand All @@ -213,16 +218,16 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion
{
return true;
}

Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddress);
MethodDescVersioningStateFlags flags = (MethodDescVersioningStateFlags)versioningState.Flags;
return flags.HasFlag(MethodDescVersioningStateFlags.IsDefaultVersionActiveChildFlag);
}
else if (nativeCodeVersion.CodeVersionNodeAddress != TargetPointer.Null)
{
// NativeCodeVersionNode::IsActiveChildVersion
// Data.NativeCodeVersionNode codeVersion = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(nativeCodeVersion.CodeVersionNodeAddress);
// return codeVersion has flag IsActive
throw new NotImplementedException(); // TODO[cdac]: IsActiveNativeCodeVersion - explicit
Data.NativeCodeVersionNode codeVersion = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(nativeCodeVersion.CodeVersionNodeAddress);
return ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
}
else
{
Expand All @@ -232,22 +237,31 @@ private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion

private NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress)
{
TargetNUInt? ilVersionId = default;
if (methodDefActiveVersion.Module != TargetPointer.Null)
{
NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null);
if (IsActiveNativeCodeVersion(provisionalHandle))
{
return provisionalHandle;
}
else
{
throw new NotImplementedException(); // TODO[cdac]: iterate through versioning state nodes
}
}
else
{
throw new NotImplementedException(); // TODO: [cdac] find explicit il code version
// Get the explicit IL code version
Debug.Assert(methodDefActiveVersion.ILCodeVersionNode != TargetPointer.Null);
Data.ILCodeVersionNode ilCodeVersion = _target.ProcessedData.GetOrAdd<Data.ILCodeVersionNode>(methodDefActiveVersion.ILCodeVersionNode);
ilVersionId = ilCodeVersion.VersionId;
}

// Iterate through versioning state nodes and return the active one, matching any IL code version
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
return FindFirstCodeVersion(rts, md, (codeVersion) =>
{
return (!ilVersionId.HasValue || ilVersionId.Value.Value == codeVersion.ILVersionId.Value)
&& ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
});
}

}
Loading
Loading