From bb4666d8cb060f4ed1b028861cec9323a4cd7363 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:56:28 +0000 Subject: [PATCH 1/4] Add legacy cDAC extent enumeration support Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../ClrDataMethodInstance.cs | 209 +++++++++++++++++- 1 file changed, 205 insertions(+), 4 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs index 96bb383b43659c..c9445593f8e847 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -17,6 +16,60 @@ namespace Microsoft.Diagnostics.DataContractReader.Legacy; [GeneratedComClass] public sealed unsafe partial class ClrDataMethodInstance : IXCLRDataMethodInstance { + private const string InvalidExtentHandleMessage = "The handle does not reference a valid EnumMethodExtents instance."; + + private struct ClrDataAddressRange + { + public ClrDataAddress StartAddress; + public ClrDataAddress EndAddress; + } + + private sealed class EnumMethodExtents : IEnum + { + public IEnumerator Enumerator { get; } + public nuint LegacyHandle { get; set; } + + public EnumMethodExtents(ClrDataAddressRange extent) + { + Enumerator = new SingleExtentEnumerator(extent); + } + } + + private sealed class SingleExtentEnumerator : IEnumerator + { + private readonly ClrDataAddressRange _extent; + private bool _hasCurrent; + + public SingleExtentEnumerator(ClrDataAddressRange extent) + { + _extent = extent; + } + + public ClrDataAddressRange Current => _hasCurrent ? _extent : throw new InvalidOperationException("Enumeration has not started or has already finished."); + + object IEnumerator.Current => Current; + + public bool MoveNext() + { + if (_hasCurrent) + { + return false; + } + + _hasCurrent = true; + return true; + } + + public void Reset() + { + _hasCurrent = false; + } + + public void Dispose() + { + } + } + private readonly Target _target; private readonly MethodDescHandle _methodDesc; private readonly TargetPointer _appDomain; @@ -372,14 +425,162 @@ int IXCLRDataMethodInstance.GetILAddressMap(uint mapLen, uint* mapNeeded, [In, O return hr; } + private ClrDataAddressRange GetMethodExtent() + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TargetCodePointer nativeCode = rts.GetNativeCode(_methodDesc); + TargetCodePointer code = _target.Contracts.PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent(nativeCode); + if (code == TargetCodePointer.Null) + { + code = nativeCode; + } + + if (code == TargetCodePointer.Null) + { + throw new InvalidOperationException($"Method descriptor has no valid native code pointer (methodDesc={_methodDesc.Address:x}; the method may not be JIT-compiled yet)."); + } + + IExecutionManager executionManager = _target.Contracts.ExecutionManager; + CodeBlockHandle? codeBlock = executionManager.GetCodeBlockHandle(code); + if (codeBlock is null) + { + throw new InvalidOperationException($"No code block found for native code address {code.ToClrDataAddress(_target):x} (the address may be invalid or the corresponding module may not be loaded)."); + } + + executionManager.GetGCInfo(codeBlock.Value, out TargetPointer gcInfoAddress, out uint gcVersion); + CodeKind codeKind = executionManager.GetCodeKind(code); + IGCInfo gcInfo = _target.Contracts.GCInfo; + IGCInfoHandle gcInfoHandle = codeKind == CodeKind.Interpreter + ? gcInfo.DecodeInterpreterGCInfo(gcInfoAddress, gcVersion) + : gcInfo.DecodePlatformSpecificGCInfo(gcInfoAddress, gcVersion); + + ClrDataAddress startAddress = code.ToClrDataAddress(_target); + uint codeLength = gcInfo.GetCodeLength(gcInfoHandle); + return new ClrDataAddressRange + { + StartAddress = startAddress, + EndAddress = startAddress + codeLength, + }; + } + int IXCLRDataMethodInstance.StartEnumExtents(ulong* handle) - => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.StartEnumExtents(handle) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + try + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + + EnumMethodExtents extents = new(GetMethodExtent()); + *handle = (ulong)((IEnum)extents).GetHandle(); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null) + { + ulong legacyHandle = 0; + int hrLocal = _legacyImpl.StartEnumExtents(handle is null ? null : &legacyHandle); + Debug.ValidateHResult(hr, hrLocal); + + if (hr == HResults.S_OK && hrLocal == HResults.S_OK) + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + ((EnumMethodExtents)gcHandle.Target!).LegacyHandle = (nuint)legacyHandle; + } + else if (hrLocal == HResults.S_OK) + { + _legacyImpl.EndEnumExtents(legacyHandle); + } + } +#endif + + return hr; + } int IXCLRDataMethodInstance.EnumExtent(ulong* handle, void* extent) - => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EnumExtent(handle, extent) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + EnumMethodExtents? extents = null; + try + { + if (handle is null) + throw new ArgumentNullException(nameof(handle)); + if (extent is null) + throw new ArgumentNullException(nameof(extent)); + if (*handle == 0) + throw new ArgumentException(InvalidExtentHandleMessage, nameof(handle)); + + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)(*handle)); + object? target = gcHandle.Target; + extents = target as EnumMethodExtents ?? throw new ArgumentException(InvalidExtentHandleMessage, nameof(handle)); + if (extents.Enumerator.MoveNext()) + { + *(ClrDataAddressRange*)extent = extents.Enumerator.Current; + } + else + { + hr = HResults.S_FALSE; + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null && extents is { LegacyHandle: not 0 }) + { + ulong legacyHandle = (ulong)extents.LegacyHandle; + ClrDataAddressRange extentLocal = default; + int hrLocal = _legacyImpl.EnumExtent(&legacyHandle, &extentLocal); + extents.LegacyHandle = (nuint)legacyHandle; + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ClrDataAddressRange* result = (ClrDataAddressRange*)extent; + Debug.Assert(result->StartAddress == extentLocal.StartAddress, $"StartAddress - cDAC: {result->StartAddress:x}, DAC: {extentLocal.StartAddress:x}"); + Debug.Assert(result->EndAddress == extentLocal.EndAddress, $"EndAddress - cDAC: {result->EndAddress:x}, DAC: {extentLocal.EndAddress:x}"); + } + } +#endif + + return hr; + } int IXCLRDataMethodInstance.EndEnumExtents(ulong handle) - => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.EndEnumExtents(handle) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + nuint legacyHandle = 0; + try + { + if (handle != 0) + { + GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)handle); + EnumMethodExtents extents = gcHandle.Target as EnumMethodExtents ?? throw new ArgumentException(InvalidExtentHandleMessage, nameof(handle)); + legacyHandle = extents.LegacyHandle; + ((IEnum)extents).Dispose(); + gcHandle.Free(); + } + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + +#if DEBUG + if (_legacyImpl is not null && legacyHandle != 0) + { + int hrLocal = _legacyImpl.EndEnumExtents((ulong)legacyHandle); + Debug.ValidateHResult(hr, hrLocal); + } +#endif + + return hr; + } int IXCLRDataMethodInstance.Request(uint reqCode, uint inBufferSize, byte* inBuffer, uint outBufferSize, byte* outBuffer) => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) : HResults.E_NOTIMPL; From d51fc12228cf351a68b1de8eec417afe294e69d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 05:24:42 +0000 Subject: [PATCH 2/4] Simplify single method extent enumeration Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- .../ClrDataMethodInstance.cs | 39 +------------------ 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs index c9445593f8e847..ba72ba57e13098 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/ClrDataMethodInstance.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -31,42 +31,7 @@ private sealed class EnumMethodExtents : IEnum public EnumMethodExtents(ClrDataAddressRange extent) { - Enumerator = new SingleExtentEnumerator(extent); - } - } - - private sealed class SingleExtentEnumerator : IEnumerator - { - private readonly ClrDataAddressRange _extent; - private bool _hasCurrent; - - public SingleExtentEnumerator(ClrDataAddressRange extent) - { - _extent = extent; - } - - public ClrDataAddressRange Current => _hasCurrent ? _extent : throw new InvalidOperationException("Enumeration has not started or has already finished."); - - object IEnumerator.Current => Current; - - public bool MoveNext() - { - if (_hasCurrent) - { - return false; - } - - _hasCurrent = true; - return true; - } - - public void Reset() - { - _hasCurrent = false; - } - - public void Dispose() - { + Enumerator = Enumerable.Repeat(extent, 1).GetEnumerator(); } } From 2271bbd488a12b3b821d5c121b733c8947aad8be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 05:30:02 +0000 Subject: [PATCH 3/4] Validate cDAC enumerator simplification Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- test_enum.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 test_enum.cs diff --git a/test_enum.cs b/test_enum.cs new file mode 100644 index 00000000000000..88b002a6dd4883 --- /dev/null +++ b/test_enum.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; + +class Program +{ + static void Main() + { + var e = Enumerable.Repeat("test", 1).GetEnumerator(); + try { Console.WriteLine("Before: " + e.Current); } catch (Exception ex) { Console.WriteLine("Before exception: " + ex.GetType().Name); } + Console.WriteLine("Move1: " + e.MoveNext()); + Console.WriteLine("After1: " + e.Current); + Console.WriteLine("Move2: " + e.MoveNext()); + Console.WriteLine("After2: " + e.Current); + } +} From 05aadd502ab948a3f11907c311682e2d5a645df3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Jul 2026 05:31:05 +0000 Subject: [PATCH 4/4] Remove temporary review artifact Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> --- test_enum.cs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 test_enum.cs diff --git a/test_enum.cs b/test_enum.cs deleted file mode 100644 index 88b002a6dd4883..00000000000000 --- a/test_enum.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Linq; - -class Program -{ - static void Main() - { - var e = Enumerable.Repeat("test", 1).GetEnumerator(); - try { Console.WriteLine("Before: " + e.Current); } catch (Exception ex) { Console.WriteLine("Before exception: " + ex.GetType().Name); } - Console.WriteLine("Move1: " + e.MoveNext()); - Console.WriteLine("After1: " + e.Current); - Console.WriteLine("Move2: " + e.MoveNext()); - Console.WriteLine("After2: " + e.Current); - } -}