File: Contracts\ExecutionManager\ExecutionManagerCore.EEJitManager.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal partial class ExecutionManagerCore<T> : IExecutionManager
{
    private sealed class EEJitManager : JitManager
    {
        private readonly INibbleMap _nibbleMap;
        private readonly RuntimeFunctionLookup _runtimeFunctions;
        public EEJitManager(Target target, INibbleMap nibbleMap) : base(target)
        {
            _nibbleMap = nibbleMap;
            _runtimeFunctions = RuntimeFunctionLookup.Create(target);
        }

        public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info)
        {
            info = null;
            // EEJitManager::JitCodeToMethodInfo
            Debug.Assert(!rangeSection.IsRangeList);

            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress);
            if (codeStart == TargetPointer.Null)
                return false;

            Debug.Assert(codeStart.Value <= jittedCodeAddress.Value);
            TargetNUInt relativeOffset = new TargetNUInt(jittedCodeAddress.Value - codeStart.Value);

            if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
                return false;

            info = new CodeBlock(codeStart, realCodeHeader.MethodDesc, relativeOffset, rangeSection.Data!.JitManager);
            return true;
        }

        public override void GetMethodRegionInfo(
            RangeSection rangeSection,
            TargetCodePointer jittedCodeAddress,
            out uint hotSize,
            out TargetPointer coldStart,
            out uint coldSize)
        {
            // cold regions are not supported for JITted code
            coldStart = TargetPointer.Null;
            coldSize = 0;

            IGCInfo gcInfo = Target.Contracts.GCInfo;
            GetGCInfo(rangeSection, jittedCodeAddress, out TargetPointer pGcInfo, out uint gcVersion);
            IGCInfoHandle gcInfoHandle = gcInfo.DecodePlatformSpecificGCInfo(pGcInfo, gcVersion);
            hotSize = gcInfo.GetCodeLength(gcInfoHandle);
            Debug.Assert(hotSize > 0);
        }

        public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress)
        {
            Debug.Assert(!rangeSection.IsRangeList);
            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress);
            if (codeStart == TargetPointer.Null)
                return TargetPointer.Null;
            Debug.Assert(codeStart.Value <= jittedCodeAddress.Value);

            if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
                return TargetPointer.Null;

            if (realCodeHeader.NumUnwindInfos == 0)
            {
                return TargetPointer.Null;
            }

            // Find the relative address that we are looking for
            TargetPointer addr = CodePointerUtils.AddressFromCodePointer(jittedCodeAddress, Target);
            TargetPointer imageBase = rangeSection.Data.RangeBegin;
            TargetPointer relativeAddr = addr - imageBase;

            if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(realCodeHeader.UnwindInfos, realCodeHeader.NumUnwindInfos, relativeAddr, out uint index))
                return TargetPointer.Null;

            return _runtimeFunctions.GetRuntimeFunctionAddress(realCodeHeader.UnwindInfos, index);
        }

        public override TargetPointer GetDebugInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out bool hasFlagByte)
        {
            hasFlagByte = false;
            Debug.Assert(!rangeSection.IsRangeList);
            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress);
            if (codeStart == TargetPointer.Null)
                return TargetPointer.Null;
            Debug.Assert(codeStart.Value <= jittedCodeAddress.Value);

            if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
                return TargetPointer.Null;

            bool featureOnStackReplacement = Target.ReadGlobal<byte>(Constants.Globals.FeatureOnStackReplacement) != 0;
            Data.EEJitManager eeJitManager = Target.ProcessedData.GetOrAdd<Data.EEJitManager>(rangeSection.Data.JitManager);
            if (featureOnStackReplacement || eeJitManager.StoreRichDebugInfo)
                hasFlagByte = true;

            return realCodeHeader.DebugInfo;
        }

        public override CodeKind GetCodeKind(RangeSection rangeSection, TargetCodePointer codeAddress)
        {
            TargetPointer startAddr = FindMethodCode(rangeSection, codeAddress); // validate that the code address is within the method's code range
            if (startAddr == TargetPointer.Null)
                return CodeKind.Unknown;
            return GetCodeHeaderStubKind(rangeSection, startAddr);
        }

        public override void GetGCInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, out TargetPointer gcInfo, out uint gcVersion)
        {
            gcInfo = TargetPointer.Null;
            gcVersion = 0;

            // EEJitManager::GetGCInfoToken
            Debug.Assert(!rangeSection.IsRangeList);

            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            TargetPointer codeStart = FindMethodCode(rangeSection, jittedCodeAddress);
            if (codeStart == TargetPointer.Null)
                return;
            Debug.Assert(codeStart.Value <= jittedCodeAddress.Value);

            if (!GetRealCodeHeader(rangeSection, codeStart, out Data.RealCodeHeader? realCodeHeader))
                return;

            gcVersion = Target.ReadGlobal<uint>(Constants.Globals.GCInfoVersion);
            gcInfo = realCodeHeader.GCInfo;
        }

        private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointer codeAddress)
        {
            // EEJitManager::FindMethodCode
            Debug.Assert(rangeSection.Data != null);

            if (!rangeSection.IsCodeHeap)
                throw new InvalidOperationException("RangeSection is not a code heap");

            TargetPointer heapListAddress = rangeSection.Data.HeapList;
            Data.CodeHeapListNode heapListNode = Target.ProcessedData.GetOrAdd<Data.CodeHeapListNode>(heapListAddress);
            return _nibbleMap.FindMethodCode(heapListNode, codeAddress);
        }

        private TargetPointer GetCodeHeaderAddress(RangeSection rangeSection, TargetPointer codeStart)
        {
            // EEJitManager::JitCodeToMethodInfo
            Debug.Assert(!rangeSection.IsRangeList);

            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            // See EEJitManager::GetCodeHeaderFromStartAddress in vm/codeman.h
            int codeHeaderOffset = Target.PointerSize;
            TargetPointer codeHeaderIndirect = new TargetPointer(codeStart - (ulong)codeHeaderOffset);
            return Target.ReadPointer(codeHeaderIndirect);
        }

        private bool GetRealCodeHeader(RangeSection rangeSection, TargetPointer codeStart, [NotNullWhen(true)] out Data.RealCodeHeader? realCodeHeader)
        {
            realCodeHeader = null;
            TargetPointer codeHeaderAddress = GetCodeHeaderAddress(rangeSection, codeStart);
            if (RangeSection.IsStubCodeBlock(Target, codeHeaderAddress))
            {
                return false;
            }
            realCodeHeader = Target.ProcessedData.GetOrAdd<Data.RealCodeHeader>(codeHeaderAddress);
            return true;
        }

        private CodeKind GetCodeHeaderStubKind(RangeSection rangeSection, TargetPointer codeStart)
        {
            TargetPointer codeHeaderAddress = GetCodeHeaderAddress(rangeSection, codeStart);
            if (RangeSection.IsStubCodeBlock(Target, codeHeaderAddress))
            {
                return GetStubKind((StubKind)codeHeaderAddress.Value);
            }
            return CodeKind.Jitted;
        }

        public override void GetExceptionClauses(RangeSection rangeSection, CodeBlockHandle codeInfoHandle, out TargetPointer startAddr, out TargetPointer endAddr)
        {
            startAddr = TargetPointer.Null;
            endAddr = TargetPointer.Null;

            if (rangeSection.Data == null)
                throw new ArgumentException(nameof(rangeSection));

            Data.RealCodeHeader? realCodeHeader;
            TargetPointer codeStart = FindMethodCode(rangeSection, new TargetCodePointer(codeInfoHandle.Address));
            if (codeStart == TargetPointer.Null)
                return;
            if (!GetRealCodeHeader(rangeSection, codeStart, out realCodeHeader) || realCodeHeader == null)
                return;

            if (realCodeHeader.EHInfo == TargetPointer.Null)
                return;

            Data.EEILException ehInfo = Target.ProcessedData.GetOrAdd<Data.EEILException>(realCodeHeader.EHInfo);
            TargetNUInt numEHInfos = Target.ReadNUInt(ehInfo.Address - (ulong)Target.PointerSize);
            startAddr = ehInfo.Clauses;
            endAddr = startAddr + numEHInfos.Value * Target.GetTypeInfo(DataType.EEExceptionClause).Size!.Value;
        }
    }
}