From 8df428cd161f2506ba4ab14187c9e9832aa7adb0 Mon Sep 17 00:00:00 2001 From: Max Charlamb Date: Fri, 24 Apr 2026 15:41:22 -0400 Subject: [PATCH] Add diagnostic logging to GetMethodTableName assertion Add Console.Error.WriteLine before Debug.Assert in GetMethodTableName to capture both the cDAC and legacy DAC type names when they differ. The stderr output survives the process crash and appears in CI test logs, allowing identification of the specific type causing the mismatch. Also adds ValidateOutputStringBuffer helper to DebugExtensions for future use in converting other assertions to soft-fail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DebugExtensions.cs | 31 +++++++++++++++++++ .../SOSDacImpl.cs | 7 +++++ 2 files changed, 38 insertions(+) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/DebugExtensions.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/DebugExtensions.cs index b5e348bdbf3d53..50ac0cf9803808 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/DebugExtensions.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/DebugExtensions.cs @@ -1,6 +1,7 @@ // 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.IO; using System.Runtime.CompilerServices; @@ -52,5 +53,35 @@ internal static void ValidateHResult( }; Debug.Assert(match, $"HResult mismatch - cDAC: 0x{unchecked((uint)cdacHr):X8}, DAC: 0x{unchecked((uint)dacHr):X8} ({Path.GetFileName(filePath)}:{lineNumber})"); } + + [Conditional("DEBUG")] + internal static unsafe void ValidateOutputStringBuffer( + char* cdacBuffer, + uint* cdacNeeded, + char[] dacBuffer, + uint dacNeeded, + uint count, + [CallerFilePath] string? filePath = null, + [CallerLineNumber] int lineNumber = 0) + { + string location = $"{Path.GetFileName(filePath)}:{lineNumber}"; + + if (cdacNeeded is not null && *cdacNeeded != dacNeeded) + { + int cdacLen = (int)Math.Min(*cdacNeeded, count); + string cdacStr = cdacBuffer is not null && cdacLen > 0 ? new string(cdacBuffer, 0, cdacLen - 1) : ""; + string dacStr = dacNeeded > 0 ? new string(dacBuffer, 0, (int)dacNeeded - 1) : ""; + Trace.TraceWarning($"Output buffer pNeeded mismatch ({location}) - cDAC: {*cdacNeeded} (\"{cdacStr}\"), DAC: {dacNeeded} (\"{dacStr}\")"); + } + else if (cdacBuffer is not null && dacNeeded > 0 && count >= dacNeeded) + { + var cdacSpan = new ReadOnlySpan(cdacBuffer, (int)dacNeeded - 1); + var dacSpan = new ReadOnlySpan(dacBuffer, 0, (int)dacNeeded - 1); + if (!cdacSpan.SequenceEqual(dacSpan)) + { + Trace.TraceWarning($"Output buffer content mismatch ({location}) - cDAC: \"{new string(cdacBuffer, 0, (int)dacNeeded - 1)}\", DAC: \"{new string(dacBuffer, 0, (int)dacNeeded - 1)}\""); + } + } + } } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index d67adff3ab3489..1e943235506586 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -2995,6 +2995,13 @@ int ISOSDacInterface.GetMethodTableName(ClrDataAddress mt, uint count, char* mtN Debug.ValidateHResult(hr, hrLocal); if (hr == HResults.S_OK) { + if (pNeeded != null && *pNeeded != neededLocal) + { + string cdacStr = mtName is not null && count > 0 ? new string(mtName, 0, (int)System.Math.Min(*pNeeded, count) - 1) : ""; + string dacStr = neededLocal > 0 ? new string(mtNameLocal, 0, (int)neededLocal - 1) : ""; + System.Console.Error.WriteLine($"CDAC_MISMATCH GetMethodTableName MT=0x{(ulong)mt:X} pNeeded: cDAC={*pNeeded} DAC={neededLocal} cDAC_name=\"{cdacStr}\" DAC_name=\"{dacStr}\""); + System.Console.Error.Flush(); + } Debug.Assert(pNeeded == null || *pNeeded == neededLocal); Debug.Assert(mtName == null || new ReadOnlySpan(mtNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(mtName))); }