Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Runtime.Utilities;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Threading;
using Microsoft.Diagnostics.Runtime;
using SharedTypes;
using Xunit;
using static ComInterfaceGenerator.Tests.UnmanagedToManagedCustomMarshallingTests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.IO;
using System.Security.Cryptography.SLHDsa.Tests;
using System.Security.Cryptography.Tests;
using Microsoft.Diagnostics.Runtime.Interop;
using Test.Cryptography;
using Xunit;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class AsyncContinuationDumpTests : DumpTestBase
{
protected override string DebuggeeName => "AsyncContinuation";
protected override string DumpType => "full";

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
Expand Down
1 change: 0 additions & 1 deletion src/native/managed/cdac/tests/DumpTests/CCWDumpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class CCWDumpTests : DumpTestBase
{
protected override string DebuggeeName => "CCW";
protected override string DumpType => "full";

/// <summary>
/// Enumerates all strong GC handles from the dump, dereferences each one to get
Expand Down
85 changes: 81 additions & 4 deletions src/native/managed/cdac/tests/DumpTests/ClrMdDumpHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Reflection.PortableExecutable;
using System.IO;
using Microsoft.Diagnostics.Runtime;

namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
Expand All @@ -20,22 +23,30 @@ internal sealed class ClrMdDumpHost : IDisposable
};

private readonly DataTarget _dataTarget;
private readonly string[] _searchPaths;

public string DumpPath { get; }

private ClrMdDumpHost(string dumpPath, DataTarget dataTarget)
private ClrMdDumpHost(string dumpPath, DataTarget dataTarget, string[] searchPaths)
{
DumpPath = dumpPath;
_dataTarget = dataTarget;
_searchPaths = searchPaths;
}

/// <summary>
/// Open a crash dump and prepare it for cDAC analysis.
/// </summary>
public static ClrMdDumpHost Open(string dumpPath)
/// <param name="dumpPath">Path to the crash dump file.</param>
/// <param name="additionalSymbolPaths">
/// Local directories to search for symbol files (e.g., System.Private.CoreLib,
/// debuggee DLLs).
/// </param>
public static ClrMdDumpHost Open(string dumpPath, List<string> additionalSymbolPaths)
{
DataTarget dataTarget = DataTarget.LoadDump(dumpPath);
return new ClrMdDumpHost(dumpPath, dataTarget);

return new ClrMdDumpHost(dumpPath, dataTarget, additionalSymbolPaths.ToArray());
}
Comment thread
rcj1 marked this conversation as resolved.

/// <summary>
Expand All @@ -45,7 +56,45 @@ public static ClrMdDumpHost Open(string dumpPath)
public int ReadFromTarget(ulong address, Span<byte> buffer)
{
int bytesRead = _dataTarget.DataReader.Read(address, buffer);
return bytesRead == buffer.Length ? 0 : -1;
if (bytesRead == buffer.Length)
return 0; // success

// If we couldn't read the full buffer, maybe it's in a PE image
Comment thread
rcj1 marked this conversation as resolved.
ModuleInfo? info = GetModuleForAddress(address);
if (info is null || info.FileName is null)
Comment thread
rcj1 marked this conversation as resolved.
{
return -1;
Comment thread
rcj1 marked this conversation as resolved.
}
Comment thread
rcj1 marked this conversation as resolved.

string? foundFile = FindFileOnDisk(info.FileName);
Comment thread
rcj1 marked this conversation as resolved.
if (foundFile is null)
{
return -1;
Comment thread
rcj1 marked this conversation as resolved.
}

using FileStream fs = File.OpenRead(foundFile);
using PEReader peReader = new PEReader(fs);

Comment thread
hoyosjs marked this conversation as resolved.
int filled = bytesRead;
ulong current = address + (ulong)bytesRead;
while (filled < buffer.Length)
{
PEMemoryBlock block = peReader.GetSectionData((int)(current - info.ImageBase));
Comment thread
rcj1 marked this conversation as resolved.
if (block.Length == 0)
{
return -1;
}

int toCopy = Math.Min(block.Length, buffer.Length - filled);
unsafe
{
new ReadOnlySpan<byte>(block.Pointer, toCopy).CopyTo(buffer.Slice(filled));
}
filled += toCopy;
current += (ulong)toCopy;
}
Comment thread
rcj1 marked this conversation as resolved.

return 0;
}
Comment thread
rcj1 marked this conversation as resolved.

/// <summary>
Expand All @@ -57,6 +106,16 @@ public int GetThreadContext(uint threadId, uint contextFlags, Span<byte> buffer)
return _dataTarget.DataReader.GetThreadContext(threadId, contextFlags, buffer) ? 0 : -1;
}

private ModuleInfo? GetModuleForAddress(ulong address)
{
foreach (ModuleInfo module in _dataTarget.DataReader.EnumerateModules())
{
if (address >= module.ImageBase && address < module.ImageBase + (ulong)module.ImageSize)
return module;
}
return null;
}
Comment thread
rcj1 marked this conversation as resolved.

/// <summary>
/// Locate the DotNetRuntimeContractDescriptor symbol address in the dump.
/// Uses ClrMD's built-in export resolution which handles PE, ELF, and Mach-O formats.
Expand Down Expand Up @@ -93,6 +152,24 @@ public ulong FindContractDescriptorAddress()
throw new InvalidOperationException("Could not find DotNetRuntimeContractDescriptor export in any runtime module in the dump.");
}

private string? FindFileOnDisk(string modulePath)
{
// for local runs
if (File.Exists(modulePath))
return modulePath;
int lastSep = Math.Max(modulePath.LastIndexOf('/'), modulePath.LastIndexOf('\\'));
Comment thread
rcj1 marked this conversation as resolved.
string fileName = lastSep >= 0 ? modulePath[(lastSep + 1)..] : modulePath;

foreach (string searchPath in _searchPaths)
{
string candidate = Path.Combine(searchPath, fileName);
if (File.Exists(candidate))
return candidate;
}
Comment thread
rcj1 marked this conversation as resolved.

return null;
}

private static bool IsRuntimeModule(string fileName)
{
foreach (string name in s_runtimeModuleNames)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class ComWrappersDumpTests : DumpTestBase
{
protected override string DebuggeeName => "ComWrappers";
protected override string DumpType => "full";

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class DacDbiCCWDumpTests : DumpTestBase
{
protected override string DebuggeeName => "CCW";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

private (TargetPointer Ccw, TargetPointer InterfacePointer) FindBuiltInComCcwWithInterface()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class DacDbiComWrappersDumpTests : DumpTestBase
{
protected override string DebuggeeName => "ComWrappers";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

[ConditionalTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class DacDbiLoaderDumpTests : DumpTestBase
{
protected override string DebuggeeName => "MultiModule";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

private IEnumerable<ModuleHandle> GetAllModules()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class DacDbiRCWDumpTests : DumpTestBase
{
protected override string DebuggeeName => "RCW";
protected override string DumpType => "full";

private DacDbiImpl CreateDacDbi() => new DacDbiImpl(Target, legacyObj: null);

[ConditionalTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<Features>$(Features);runtime-async=on</Features>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<NoWarn>$(NoWarn);CA2007;CA2252</NoWarn>
<!-- Full dump needed so that module metadata is available for type lookup -->
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
<WindowsOnly>true</WindowsOnly>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
<R2RModes>R2R;Jit</R2RModes>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
<!-- local variables don't display correctly in R2R dumps -->
<R2RModes>Jit</R2RModes>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
Comment thread
rcj1 marked this conversation as resolved.
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<!-- This debuggee is intentionally Windows-only (COM interop). -->
<NoWarn>$(NoWarn);CA1416</NoWarn>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
<WindowsOnly>true</WindowsOnly>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<!-- This debuggee is intentionally Windows-only (COM interop). -->
<NoWarn>$(NoWarn);CA1416</NoWarn>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
<WindowsOnly>true</WindowsOnly>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
<R2RModes>R2R;Jit</R2RModes>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DumpTypes>Full</DumpTypes>
<DumpTypes>Heap</DumpTypes>
Comment thread
rcj1 marked this conversation as resolved.
</PropertyGroup>
Comment thread
rcj1 marked this conversation as resolved.
</Project>
26 changes: 25 additions & 1 deletion src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ protected void InitializeDumpTest(TestConfiguration config, string debuggeeName,
throw new SkipTestException($"No {config.R2RMode} dump for {debuggeeName}: {dumpPath}");
}

_host = ClrMdDumpHost.Open(dumpPath);
_host = ClrMdDumpHost.Open(dumpPath, GetSymbolPaths(debuggeeName, versionDir));
ulong contractDescriptor = _host.FindContractDescriptorAddress();

Comment thread
rcj1 marked this conversation as resolved.
bool created = ContractDescriptorTarget.TryCreate(
Expand Down Expand Up @@ -245,6 +245,30 @@ private static bool IsVersionSkipped(string version)
return false;
}

/// <summary>
/// Collects local symbol paths for ClrMD to resolve modules in the dump.
/// Checks <c>symbols/</c> directories in the helix payload (Helix and xplat dumps)
/// </summary>
private static List<string> GetSymbolPaths(string debuggeeName, string versionDir)
{
List<string> paths = [];

// Symbols directory in the dump tree (populated by Helix commands before tarring)
string symbolsDir = Path.Combine(versionDir, "symbols");
if (Directory.Exists(symbolsDir))
{
string runtimeSymbols = Path.Combine(symbolsDir, "runtime");
if (Directory.Exists(runtimeSymbols))
paths.Add(runtimeSymbols);

string debuggeeSymbols = Path.Combine(symbolsDir, "debuggees", debuggeeName);
if (Directory.Exists(debuggeeSymbols))
paths.Add(debuggeeSymbols);
}

return paths;
}

private static string? FindRepoRoot()
{
string? dir = AppContext.BaseDirectory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class EcmaMetadataDumpTests : DumpTestBase
{
protected override string DebuggeeName => "MultiModule";
Comment thread
rcj1 marked this conversation as resolved.
protected override string DumpType => "full";

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class ExceptionHandlingInfoDumpTests : DumpTestBase
{
protected override string DebuggeeName => "ExceptionHandlingInfo";
protected override string DumpType => "full";

/// <summary>
/// Finds the CodeBlockHandle for the CrashInExceptionHandler method by walking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public class ISOSDacInterface13Tests : DumpTestBase
{
protected override string DebuggeeName => "MultiModule";
Comment thread
rcj1 marked this conversation as resolved.
protected override string DumpType => "full";

[ConditionalTheory]
[MemberData(nameof(TestConfigurations))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public unsafe class IXCLRDataAppDomainDumpTests : DumpTestBase
{
protected override string DebuggeeName => "StackWalk";
protected override string DumpType => "full";

// ========== GetName ==========

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public unsafe class IXCLRDataFrameDumpTests : DumpTestBase
{
protected override string DebuggeeName => "StackWalk";
protected override string DumpType => "full";

// ========== GetContext ==========

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public unsafe class IXCLRDataMethodDefinitionDumpTests : DumpTestBase
{
protected override string DebuggeeName => "StackWalk";
protected override string DumpType => "full";

// ========== GetName ==========

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
public unsafe class IXCLRDataValueDumpTests : DumpTestBase
{
protected override string DebuggeeName => "LocalVariables";
protected override string DumpType => "full";

// ========== GetSize ==========

Expand Down
Loading
Loading