| File: Contracts\DebugInfo\DebugInfo_2.cs | Web Access |
| Project: 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 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 []; } IReadOnlyList<AsyncSuspensionInfo> IDebugInfo.GetAsyncSuspensionPoints(TargetCodePointer pCode) { 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 _); if (debugInfo == TargetPointer.Null) return Array.Empty<AsyncSuspensionInfo>(); DebugInfoChunks chunks = DecodeChunks(debugInfo); if (chunks.AsyncInfoSize == 0) return Array.Empty<AsyncSuspensionInfo>(); NativeReader asyncNativeReader = new(new TargetStream(_target, chunks.AsyncInfoStart, chunks.AsyncInfoSize), _target.IsLittleEndian); return DebugInfoHelpers.DoAsyncInfo(asyncNativeReader); } }