File: Contracts\DebugInfo\DebugInfo_1.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.Collections.Generic;
using System.Diagnostics;
using ILCompiler.Reflection.ReadyToRun;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal sealed class DebugInfo_1(Target target) : IDebugInfo
{
    private const uint DEBUG_INFO_BOUNDS_HAS_INSTRUMENTED_BOUNDS = 0xFFFFFFFF;

    [Flags]
    internal enum ExtraDebugInfoFlags_1 : byte
    {
        // Debug info contains patchpoint information
        EXTRA_DEBUG_INFO_PATCHPOINT = 0x01,
        // Debug info contains rich information
        EXTRA_DEBUG_INFO_RICH = 0x02,
    }

    private readonly Target _target = target;
    private readonly IExecutionManager _eman = target.Contracts.ExecutionManager;

    bool IDebugInfo.HasDebugInfo(TargetCodePointer pCode)
    {
        if (_eman.GetCodeBlockHandle(pCode) is not CodeBlockHandle cbh)
            return false;

        return _eman.GetDebugInfo(cbh, out _) != TargetPointer.Null;
    }

    IEnumerable<OffsetMapping> IDebugInfo.GetMethodNativeMap(TargetCodePointer pCode, bool preferUninstrumented, out uint codeOffset)
    {
        // Get the method's DebugInfo
        if (_eman.GetCodeBlockHandle(pCode) is not CodeBlockHandle cbh)
            throw new InvalidOperationException($"No CodeBlockHandle found for native code {pCode}.");
        TargetPointer debugInfo = _eman.GetDebugInfo(cbh, out bool hasFlagByte);

        TargetPointer nativeCodeStart = _eman.GetStartAddress(cbh);
        codeOffset = (uint)(CodePointerUtils.AddressFromCodePointer(pCode, _target) - nativeCodeStart);

        if (debugInfo == TargetPointer.Null)
            return [];

        return RestoreBoundaries(debugInfo, hasFlagByte, preferUninstrumented);
    }

    private IEnumerable<OffsetMapping> RestoreBoundaries(TargetPointer debugInfo, bool hasFlagByte, bool preferUninstrumented)
    {
        if (hasFlagByte)
        {
            // Check flag byte and skip over any patchpoint info
            ExtraDebugInfoFlags_1 flagByte = (ExtraDebugInfoFlags_1)_target.Read<byte>(debugInfo++);

            if (flagByte.HasFlag(ExtraDebugInfoFlags_1.EXTRA_DEBUG_INFO_PATCHPOINT))
            {
                Data.PatchpointInfo patchpointInfo = _target.ProcessedData.GetOrAdd<Data.PatchpointInfo>(debugInfo);

                if (_target.GetTypeInfo(DataType.PatchpointInfo).Size is not uint patchpointSize)
                    throw new InvalidOperationException("PatchpointInfo type size is not defined.");
                debugInfo += patchpointSize + (patchpointInfo.LocalCount * sizeof(uint));

                flagByte &= ~ExtraDebugInfoFlags_1.EXTRA_DEBUG_INFO_PATCHPOINT;
            }

            if (flagByte.HasFlag(ExtraDebugInfoFlags_1.EXTRA_DEBUG_INFO_RICH))
            {
                uint richDebugInfoSize = _target.Read<uint>(debugInfo);
                debugInfo += 4;
                debugInfo += richDebugInfoSize;
                flagByte &= ~ExtraDebugInfoFlags_1.EXTRA_DEBUG_INFO_RICH;
            }

            Debug.Assert(flagByte == 0);
        }

        NativeReader nibbleNativeReader = new(new TargetStream(_target, debugInfo, 24 /*maximum size of 4 32bit ints compressed*/), _target.IsLittleEndian);
        NibbleReader nibbleReader = new(nibbleNativeReader, 0);

        uint cbBounds = nibbleReader.ReadUInt();
        uint cbUninstrumentedBounds = 0;
        if (cbBounds == DEBUG_INFO_BOUNDS_HAS_INSTRUMENTED_BOUNDS)
        {
            // This means we have instrumented bounds.
            cbBounds = nibbleReader.ReadUInt();
            cbUninstrumentedBounds = nibbleReader.ReadUInt();
        }
        uint _ /*cbVars*/ = nibbleReader.ReadUInt();

        TargetPointer addrBounds = debugInfo + (uint)nibbleReader.GetNextByteOffset();
        // TargetPointer addrVars = addrBounds + cbBounds + cbUninstrumentedBounds;

        if (preferUninstrumented && cbUninstrumentedBounds != 0)
        {
            // If we have uninstrumented bounds, we will use them instead of the regular bounds.
            addrBounds += cbBounds;
            cbBounds = cbUninstrumentedBounds;
        }

        if (cbBounds > 0)
        {
            NativeReader boundsNativeReader = new(new TargetStream(_target, addrBounds, cbBounds), _target.IsLittleEndian);
            return DebugInfoHelpers.DoBounds(boundsNativeReader, 1);
        }

        return [];
    }
}