|
// 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 ILCompiler.Reflection.ReadyToRun;
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal sealed class DebugInfo_2(Target target) : IDebugInfo
{
private const uint DEBUG_INFO_FAT = 0;
private record struct DebugInfoChunks
{
public TargetPointer BoundsStart;
public uint BoundsSize;
public TargetPointer VarsStart;
public uint VarsSize;
public TargetPointer UninstrumentedBoundsStart;
public uint UninstrumentedBoundsSize;
public TargetPointer PatchpointInfoStart;
public uint PatchpointInfoSize;
public TargetPointer RichDebugInfoStart;
public uint RichDebugInfoSize;
public TargetPointer AsyncInfoStart;
public uint AsyncInfoSize;
public TargetPointer DebugInfoEnd;
}
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 _);
TargetPointer nativeCodeStart = _eman.GetStartAddress(cbh);
codeOffset = (uint)(CodePointerUtils.AddressFromCodePointer(pCode, _target) - nativeCodeStart);
if (debugInfo == TargetPointer.Null)
return [];
return RestoreBoundaries(debugInfo, preferUninstrumented);
}
private DebugInfoChunks DecodeChunks(TargetPointer debugInfo)
{
NativeReader nibbleNativeReader = new(new TargetStream(_target, debugInfo, 42 /*maximum size of 7 32bit ints compressed*/), _target.IsLittleEndian);
NibbleReader nibbleReader = new(nibbleNativeReader, 0);
uint countBoundsOrFatMarker = nibbleReader.ReadUInt();
DebugInfoChunks chunks = default;
if (countBoundsOrFatMarker == DEBUG_INFO_FAT)
{
// Fat header
chunks.BoundsSize = nibbleReader.ReadUInt();
chunks.VarsSize = nibbleReader.ReadUInt();
chunks.UninstrumentedBoundsSize = nibbleReader.ReadUInt();
chunks.PatchpointInfoSize = nibbleReader.ReadUInt();
chunks.RichDebugInfoSize = nibbleReader.ReadUInt();
chunks.AsyncInfoSize = nibbleReader.ReadUInt();
}
else
{
chunks.BoundsSize = countBoundsOrFatMarker;
chunks.VarsSize = nibbleReader.ReadUInt();
chunks.UninstrumentedBoundsSize = 0;
chunks.PatchpointInfoSize = 0;
chunks.RichDebugInfoSize = 0;
chunks.AsyncInfoSize = 0;
}
chunks.BoundsStart = debugInfo + (uint)nibbleReader.GetNextByteOffset();
chunks.VarsStart = chunks.BoundsStart + chunks.BoundsSize;
chunks.UninstrumentedBoundsStart = chunks.VarsStart + chunks.VarsSize;
chunks.PatchpointInfoStart = chunks.UninstrumentedBoundsStart + chunks.UninstrumentedBoundsSize;
chunks.RichDebugInfoStart = chunks.PatchpointInfoStart + chunks.PatchpointInfoSize;
chunks.AsyncInfoStart = chunks.RichDebugInfoStart + chunks.RichDebugInfoSize;
chunks.DebugInfoEnd = chunks.AsyncInfoStart + chunks.AsyncInfoSize;
return chunks;
}
private IEnumerable<OffsetMapping> RestoreBoundaries(TargetPointer debugInfo, bool preferUninstrumented)
{
DebugInfoChunks chunks = DecodeChunks(debugInfo);
TargetPointer addrBounds = chunks.BoundsStart;
uint cbBounds = chunks.BoundsSize;
if (preferUninstrumented && chunks.UninstrumentedBoundsSize != 0)
{
// If we have uninstrumented bounds, we will use them instead of the regular bounds.
addrBounds = chunks.UninstrumentedBoundsStart;
cbBounds = chunks.UninstrumentedBoundsSize;
}
if (cbBounds > 0)
{
NativeReader boundsNativeReader = new(new TargetStream(_target, addrBounds, cbBounds), _target.IsLittleEndian);
return DebugInfoHelpers.DoBounds(boundsNativeReader, 2);
}
return [];
}
IEnumerable<DebugVarInfo> IDebugInfo.GetMethodVarInfo(TargetCodePointer pCode, out uint codeOffset)
{
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 _);
// Compute code offset from the method's native code entry point, not from the code block start.
// GetStartAddress returns the start of the current code block (which may be a funclet for exception
// handlers). Variable location offsets are always relative to the method entry point, so we must use
// GetNativeCode from the NativeCodeVersion, matching the native DAC's GetMethodVarInfo which uses
// NativeCodeVersion::GetNativeCode() for this purpose
ICodeVersions cv = _target.Contracts.CodeVersions;
NativeCodeVersionHandle ncvh = cv.GetNativeCodeVersionForIP(pCode);
if (!ncvh.Valid)
throw new InvalidOperationException($"No NativeCodeVersion found for native code {pCode}.");
TargetCodePointer nativeCodeStart = cv.GetNativeCode(ncvh);
codeOffset = (uint)(CodePointerUtils.AddressFromCodePointer(pCode, _target) - CodePointerUtils.AddressFromCodePointer(nativeCodeStart, _target));
if (debugInfo == TargetPointer.Null)
return [];
DebugInfoChunks chunks = DecodeChunks(debugInfo);
if (chunks.VarsSize > 0)
{
bool isX86 = _target.Contracts.RuntimeInfo.GetTargetArchitecture() == RuntimeInfoArchitecture.X86;
NativeReader varsNativeReader = new(new TargetStream(_target, chunks.VarsStart, chunks.VarsSize), _target.IsLittleEndian);
return DebugInfoHelpers.DoVars(varsNativeReader, isX86);
}
return [];
}
}
|