|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Diagnostics.DataContractReader.Data;
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal readonly partial struct CodeVersions_1 : ICodeVersions
{
private readonly Target _target;
public CodeVersions_1(Target target)
{
_target = target;
}
ILCodeVersionHandle ICodeVersions.GetActiveILCodeVersion(TargetPointer methodDesc)
{
// CodeVersionManager::GetActiveILCodeVersion
GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken);
TargetPointer ilVersionStateAddress = GetILVersionStateAddress(module, methodDefToken);
if (ilVersionStateAddress == TargetPointer.Null)
{
return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken);
}
Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd<Data.ILCodeVersioningState>(ilVersionStateAddress);
return ActiveILCodeVersionHandleFromState(ilState);
}
ILCodeVersionHandle ICodeVersions.GetILCodeVersion(NativeCodeVersionHandle nativeCodeVersionHandle)
{
// NativeCodeVersion::GetILCodeVersion
if (!nativeCodeVersionHandle.Valid)
{
return ILCodeVersionHandle.Invalid;
}
if (!nativeCodeVersionHandle.IsExplicit)
{
// There is only a single synthetic NativeCodeVersion per
// method and it must be on the synthetic ILCodeVersion
GetModuleAndMethodDesc(
nativeCodeVersionHandle.MethodDescAddress,
out TargetPointer module,
out uint methodDefToken);
return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken);
}
else
{
// Otherwise filter all the ILCodeVersions for the one that matches the version id
NativeCodeVersionNode nativeCodeVersionNode = AsNode(nativeCodeVersionHandle);
foreach (ILCodeVersionHandle ilCodeVersionHandle in ((ICodeVersions)this).GetILCodeVersions(nativeCodeVersionNode.MethodDesc))
{
if (GetId(ilCodeVersionHandle) == nativeCodeVersionNode.ILVersionId)
{
return ilCodeVersionHandle;
}
}
}
return ILCodeVersionHandle.Invalid;
}
IEnumerable<ILCodeVersionHandle> ICodeVersions.GetILCodeVersions(TargetPointer methodDesc)
{
// CodeVersionManager::GetILCodeVersions
GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken);
// always add the synthetic version
yield return ILCodeVersionHandle.CreateSynthetic(module, methodDefToken);
// if explicit versions exist, iterate linked list and return them
TargetPointer ilVersionStateAddress = GetILVersionStateAddress(module, methodDefToken);
if (ilVersionStateAddress != TargetPointer.Null)
{
Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd<Data.ILCodeVersioningState>(ilVersionStateAddress);
TargetPointer nodePointer = ilState.FirstVersionNode;
while (nodePointer != TargetPointer.Null)
{
Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd<Data.ILCodeVersionNode>(nodePointer);
yield return ILCodeVersionHandle.CreateExplicit(nodePointer);
nodePointer = current.Next;
}
}
}
IEnumerable<NativeCodeVersionHandle> ICodeVersions.GetNativeCodeVersions(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle)
{
if (!ilCodeVersionHandle.IsValid)
yield break;
if (!ilCodeVersionHandle.IsExplicit)
{
// if the ILCodeVersion is synthetic, then yield the synthetic NativeCodeVersion
NativeCodeVersionHandle provisionalHandle = NativeCodeVersionHandle.CreateSynthetic(methodDesc);
yield return provisionalHandle;
}
// 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(methodDesc);
TargetNUInt ilVersionId = GetId(ilCodeVersionHandle);
IEnumerable<NativeCodeVersionHandle> nativeCodeVersions = FindNativeCodeVersionNodes(
rts,
md,
(codeVersion) => ilVersionId == codeVersion.ILVersionId);
foreach (NativeCodeVersionHandle nativeCodeVersion in nativeCodeVersions)
{
yield return nativeCodeVersion;
}
}
NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointer ip)
{
// ExecutionManager::GetNativeCodeVersion(PCODE ip))
// and EECodeInfo::GetNativeCodeVersion
Contracts.IExecutionManager executionManager = _target.Contracts.ExecutionManager;
CodeBlockHandle? info = executionManager.GetCodeBlockHandle(ip);
if (!info.HasValue)
{
return NativeCodeVersionHandle.Invalid;
}
TargetPointer methodDescAddress = executionManager.GetMethodDesc(info.Value);
if (methodDescAddress == TargetPointer.Null)
{
return NativeCodeVersionHandle.Invalid;
}
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
if (!rts.IsVersionable(md))
{
return NativeCodeVersionHandle.CreateSynthetic(methodDescAddress);
}
else
{
TargetCodePointer startAddress = CodePointerUtils.CodePointerFromAddress(
executionManager.GetStartAddress(info.Value),
_target);
return GetSpecificNativeCodeVersion(rts, md, startAddress);
}
}
bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddress)
{
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
if (rts.IsDynamicMethod(md))
return false;
if (rts.IsCollectibleMethod(md))
return false;
TargetPointer mtAddr = rts.GetMethodTable(md);
TypeHandle mt = rts.GetTypeHandle(mtAddr);
TargetPointer modAddr = rts.GetModule(mt);
ILoader loader = _target.Contracts.Loader;
ModuleHandle mod = loader.GetModuleHandleFromModulePtr(modAddr);
ModuleFlags modFlags = loader.GetFlags(mod);
if (modFlags.HasFlag(ModuleFlags.EditAndContinue))
return false;
return true;
}
TargetCodePointer ICodeVersions.GetNativeCode(NativeCodeVersionHandle codeVersionHandle)
{
if (!codeVersionHandle.Valid)
{
throw new ArgumentException("Invalid NativeCodeVersionHandle");
}
if (!codeVersionHandle.IsExplicit)
{
MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(codeVersionHandle.MethodDescAddress);
return _target.Contracts.RuntimeTypeSystem.GetNativeCode(md);
}
else
{
Data.NativeCodeVersionNode nativeCodeVersionNode = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(codeVersionHandle.CodeVersionNodeAddress);
return nativeCodeVersionNode.NativeCode;
}
}
NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle)
{
// ILCodeVersion::GetActiveNativeCodeVersion
if (!ilCodeVersionHandle.IsValid)
{
return NativeCodeVersionHandle.Invalid;
}
if (!ilCodeVersionHandle.IsExplicit)
{
// if the ILCodeVersion is synthetic, then check if the active NativeCodeVersion is the synthetic one
NativeCodeVersionHandle provisionalHandle = NativeCodeVersionHandle.CreateSynthetic(methodDescAddress: methodDesc);
if (IsActiveNativeCodeVersion(provisionalHandle))
{
return provisionalHandle;
}
}
// 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(methodDesc);
TargetNUInt ilVersionId = GetId(ilCodeVersionHandle);
return FindNativeCodeVersionNodes(rts, md, (codeVersion) =>
{
return (ilVersionId == codeVersion.ILVersionId)
&& ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
}).FirstOrDefault(NativeCodeVersionHandle.Invalid);
}
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 && gcCoverageInfoAddr != TargetPointer.Null)
{
Target.TypeInfo gcCoverageInfoType = _target.GetTypeInfo(DataType.GCCoverageInfo);
return gcCoverageInfoAddr + (ulong)gcCoverageInfoType.Fields["SavedCode"].Offset;
}
return TargetPointer.Null;
}
}
[Flags]
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
TargetCodePointer firstNativeCode = rts.GetNativeCode(md);
if (firstNativeCode == startAddress)
{
NativeCodeVersionHandle first = NativeCodeVersionHandle.CreateSynthetic(md.Address);
return first;
}
// CodeVersionManager::GetNativeCodeVersion(PTR_MethodDesc, PCODE startAddress)
return FindNativeCodeVersionNodes(rts, md, (codeVersion) =>
{
return codeVersion.MethodDesc == md.Address && codeVersion.NativeCode == startAddress;
}).FirstOrDefault(NativeCodeVersionHandle.Invalid);
}
private IEnumerable<NativeCodeVersionHandle> FindNativeCodeVersionNodes(IRuntimeTypeSystem rts, MethodDescHandle md, Func<Data.NativeCodeVersionNode, bool> predicate)
{
// ImplicitCodeVersion stage of NativeCodeVersionIterator::Next()
TargetPointer versioningStateAddr = rts.GetMethodDescVersioningState(md);
if (versioningStateAddr == TargetPointer.Null)
yield break;
Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddr);
// LinkedList stage of NativeCodeVersion::Next, heavily inlined
TargetPointer currentAddress = versioningState.NativeCodeVersionNode;
while (currentAddress != TargetPointer.Null)
{
Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(currentAddress);
if (predicate(current))
{
yield return NativeCodeVersionHandle.CreateExplicit(currentAddress);
}
currentAddress = current.Next;
}
yield break;
}
private enum ILCodeVersionKind
{
Unknown = 0,
Explicit = 1, // means Node is set
Synthetic = 2, // means Module and Token are set
}
private static ILCodeVersionHandle ActiveILCodeVersionHandleFromState(Data.ILCodeVersioningState ilState)
{
switch ((ILCodeVersionKind)ilState.ActiveVersionKind)
{
case ILCodeVersionKind.Explicit:
return ILCodeVersionHandle.CreateExplicit(ilState.ActiveVersionNode);
case ILCodeVersionKind.Synthetic:
case ILCodeVersionKind.Unknown:
return ILCodeVersionHandle.CreateSynthetic(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef);
default:
throw new InvalidOperationException($"Unknown ILCodeVersionKind {ilState.ActiveVersionKind}");
}
}
[Flags]
internal enum NativeCodeVersionNodeFlags : uint
{
IsActiveChild = 1
};
private bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion)
{
// NativeCodeVersion::IsActiveChildVersion
if (!nativeCodeVersion.Valid)
{
throw new ArgumentException("Invalid NativeCodeVersionHandle");
}
if (!nativeCodeVersion.IsExplicit)
{
MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress);
TargetPointer versioningStateAddress = _target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md);
if (versioningStateAddress == TargetPointer.Null)
{
return true;
}
Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddress);
MethodDescVersioningStateFlags flags = (MethodDescVersioningStateFlags)versioningState.Flags;
return flags.HasFlag(MethodDescVersioningStateFlags.IsDefaultVersionActiveChildFlag);
}
else
{
// NativeCodeVersionNode::IsActiveChildVersion
Data.NativeCodeVersionNode codeVersion = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(nativeCodeVersion.CodeVersionNodeAddress);
return ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
}
}
private void GetModuleAndMethodDesc(TargetPointer methodDesc, out TargetPointer module, out uint methodDefToken)
{
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle md = rts.GetMethodDescHandle(methodDesc);
TargetPointer mtAddr = rts.GetMethodTable(md);
TypeHandle typeHandle = rts.GetTypeHandle(mtAddr);
module = rts.GetModule(typeHandle);
methodDefToken = rts.GetMethodToken(md);
}
private TargetPointer GetILVersionStateAddress(TargetPointer module, uint methodDefToken)
{
// No token - for example, special runtime methods like array methods
if (methodDefToken == (uint)EcmaMetadataUtils.TokenType.mdtMethodDef)
return TargetPointer.Null;
ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(module);
TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState;
TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _);
return ilVersionStateAddress;
}
private ILCodeVersionNode AsNode(ILCodeVersionHandle handle)
{
if (handle.ILCodeVersionNode == TargetPointer.Null)
{
throw new InvalidOperationException("Synthetic ILCodeVersion does not have a backing node.");
}
return _target.ProcessedData.GetOrAdd<ILCodeVersionNode>(handle.ILCodeVersionNode);
}
private NativeCodeVersionNode AsNode(NativeCodeVersionHandle handle)
{
if (handle.CodeVersionNodeAddress == TargetPointer.Null)
{
throw new InvalidOperationException("Synthetic NativeCodeVersion does not have a backing node.");
}
return _target.ProcessedData.GetOrAdd<NativeCodeVersionNode>(handle.CodeVersionNodeAddress);
}
private TargetNUInt GetId(ILCodeVersionHandle ilCodeVersionHandle)
{
if (!ilCodeVersionHandle.IsExplicit)
{
// for non explicit ILCodeVersions, id is always 0
return new TargetNUInt(0);
}
ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle);
return ilCodeVersionNode.VersionId;
}
TargetPointer ICodeVersions.GetIL(ILCodeVersionHandle ilCodeVersionHandle)
{
TargetPointer ilAddress = default;
if (ilCodeVersionHandle.IsExplicit)
{
ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle);
ilAddress = ilCodeVersionNode.ILAddress;
}
if (ilAddress == TargetPointer.Null)
{
// Synthetic ILCodeVersion, get the IL from the module and method def
ILoader loader = _target.Contracts.Loader;
ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(ilCodeVersionHandle.Module);
ilAddress = loader.GetILHeader(moduleHandle, ilCodeVersionHandle.MethodDefinition);
}
return ilAddress;
}
bool ICodeVersions.HasDefaultIL(ILCodeVersionHandle iLCodeVersionHandle)
{
return iLCodeVersionHandle.IsExplicit ? AsNode(iLCodeVersionHandle).ILAddress == TargetPointer.Null : true;
}
OptimizationTier ICodeVersions.GetOptimizationTier(NativeCodeVersionHandle codeVersionHandle)
{
if (!codeVersionHandle.Valid)
{
throw new ArgumentException("Invalid NativeCodeVersionHandle");
}
if (codeVersionHandle.IsExplicit)
{
NativeCodeVersionNode nativeCodeVersionNode = _target.ProcessedData.GetOrAdd<NativeCodeVersionNode>(codeVersionHandle.CodeVersionNodeAddress);
return RuntimeTypeSystem_1.GetOptimizationTier(nativeCodeVersionNode.OptimizationTier);
}
else
{
IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(codeVersionHandle.MethodDescAddress);
OptimizationTier optimizationTier = rtsContract.GetMethodDescOptimizationTier(methodDescHandle);
return optimizationTier;
}
}
}
|