diff --git a/eng/Subsets.props b/eng/Subsets.props
index 2b9028d5df1d61..ccc00152a2bb07 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -165,7 +165,7 @@
$(AllSubsetsExpansion)+clr.paltests+clr.paltestlist+clr.hosts+clr.jit+clr.alljits+clr.alljitscommunity+clr.spmi+clr.corelib+clr.nativecorelib+clr.tools+clr.toolstests+clr.packages
$(AllSubsetsExpansion)+linuxdac+alpinedac
$(AllSubsetsExpansion)+mono.runtime+provision.emsdk+mono.aotcross+mono.corelib+mono.manifests+mono.packages+mono.tools+mono.wasmruntime+mono.wasiruntime+mono.wasmworkload+mono.mscordbi+mono.workloads
- $(AllSubsetsExpansion)+tools.illink+tools.cdac+tools.illinktests+tools.cdactests
+ $(AllSubsetsExpansion)+tools.illink+tools.cdac+tools.illinktests+tools.cdactests+tools.cdacdumptests
$(AllSubsetsExpansion)+host.native+host.pkg+host.tools+host.pretest+host.tests
$(AllSubsetsExpansion)+libs.native+libs.sfx+libs.oob+libs.pretest+libs.tests
$(AllSubsetsExpansion)+packs.product+packs.installers+packs.tests
@@ -253,6 +253,7 @@
+
@@ -523,6 +524,10 @@
+
+
+
+
diff --git a/eng/Versions.props b/eng/Versions.props
index bfa35f15609e3b..d1955761a0f492 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -155,6 +155,7 @@
13.0.3
1.0.2
4.18.4
+ 3.1.512801
8.0.2
2.14.3
2.9.1
diff --git a/eng/pipelines/runtime-diagnostics.yml b/eng/pipelines/runtime-diagnostics.yml
index 3fde402abd2009..83d5b83e335a1c 100644
--- a/eng/pipelines/runtime-diagnostics.yml
+++ b/eng/pipelines/runtime-diagnostics.yml
@@ -5,6 +5,13 @@ parameters:
displayName: Diagnostics Branch
type: string
default: main
+- name: cdacDumpPlatforms
+ displayName: cDAC Dump Platforms
+ type: object
+ default:
+ - windows_x64
+ - linux_x64
+ # - osx_x64 # Temporarily due to CI capacity constraints. Will re-enable once osx queues are more available.
resources:
repositories:
@@ -134,3 +141,88 @@ extends:
buildConfiguration: $(_BuildConfig)
continueOnError: true
condition: always()
+
+ #
+ # cDAC Dump Creation — Build runtime, create crash dumps, publish dump artifacts
+ #
+ - stage: DumpCreation
+ dependsOn: []
+ jobs:
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: release
+ platforms: ${{ parameters.cdacDumpPlatforms }}
+ jobParameters:
+ buildArgs: -s clr+libs+tools.cdac -c $(_BuildConfig) -rc $(_BuildConfig) -lc $(_BuildConfig)
+ nameSuffix: CdacDumpGeneration
+ timeoutInMinutes: 120
+ postBuildSteps:
+ - script: $(Build.SourcesDirectory)$(dir).dotnet$(dir)dotnet$(exeExt) msbuild
+ $(Build.SourcesDirectory)/src/native/managed/cdac/tests/DumpTests/Microsoft.Diagnostics.DataContractReader.DumpTests.csproj
+ /t:GenerateAllDumps
+ /p:CIDumpVersionsOnly=true
+ /p:SetDisableAuxProviderSignatureCheck=true
+ /p:TargetArchitecture=$(archType)
+ -bl:$(Build.SourcesDirectory)/artifacts/log/DumpGeneration.binlog
+ displayName: 'Generate cDAC Dumps'
+ - template: /eng/pipelines/common/upload-artifact-step.yml
+ parameters:
+ rootFolder: $(Build.SourcesDirectory)/artifacts/dumps/cdac
+ includeRootFolder: false
+ archiveType: tar
+ archiveExtension: .tar.gz
+ tarCompression: gz
+ artifactName: CdacDumps_$(osGroup)_$(archType)
+ displayName: cDAC Dump Artifacts
+
+ #
+ # cDAC Dump Tests — Download dumps from all platforms, run tests cross-platform
+ #
+ - stage: DumpTest
+ dependsOn:
+ - DumpCreation
+ jobs:
+ - template: /eng/pipelines/common/platform-matrix.yml
+ parameters:
+ jobTemplate: /eng/pipelines/common/global-build-job.yml
+ buildConfig: release
+ platforms: ${{ parameters.cdacDumpPlatforms }}
+ jobParameters:
+ buildArgs: -s tools.cdacdumptests /p:SkipDumpVersions=net10.0
+ nameSuffix: CdacDumpTests
+ timeoutInMinutes: 60
+ postBuildSteps:
+ # Download and test against dumps from each platform
+ - ${{ each dumpPlatform in parameters.cdacDumpPlatforms }}:
+ - template: /eng/pipelines/common/download-artifact-step.yml
+ parameters:
+ artifactName: CdacDumps_${{ dumpPlatform }}
+ artifactFileName: CdacDumps_${{ dumpPlatform }}.tar.gz
+ unpackFolder: $(Build.SourcesDirectory)/artifacts/dumps/${{ dumpPlatform }}
+ displayName: '${{ dumpPlatform }} Dumps'
+ - script: $(Build.SourcesDirectory)$(dir).dotnet$(dir)dotnet$(exeExt) test
+ $(Build.SourcesDirectory)/src/native/managed/cdac/tests/DumpTests/Microsoft.Diagnostics.DataContractReader.DumpTests.csproj
+ --no-build
+ --logger "trx;LogFileName=CdacDumpTests_${{ dumpPlatform }}.trx"
+ --results-directory $(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)/${{ dumpPlatform }}
+ displayName: 'Run cDAC Dump Tests (${{ dumpPlatform }} dumps)'
+ continueOnError: true
+ env:
+ CDAC_DUMP_ROOT: $(Build.SourcesDirectory)/artifacts/dumps/${{ dumpPlatform }}
+ - task: PublishTestResults@2
+ displayName: 'Publish Results ($(osGroup)-$(archType) → ${{ dumpPlatform }})'
+ inputs:
+ testResultsFormat: VSTest
+ testResultsFiles: '**/*.trx'
+ searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)/${{ dumpPlatform }}'
+ testRunTitle: 'cDAC Dump Tests $(osGroup)-$(archType) → ${{ dumpPlatform }}'
+ failTaskOnFailedTests: true
+ publishRunAttachments: true
+ buildConfiguration: $(_BuildConfig)
+ continueOnError: true
+ condition: always()
+ # Fail the job if any test or publish step above reported issues.
+ - script: echo "One or more dump test steps failed." && exit 1
+ displayName: 'Fail if tests failed'
+ condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs
index 05fba0fc545ff8..ccdc5017b5d5b8 100644
--- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs
+++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Thread.cs
@@ -33,7 +33,10 @@ public Thread(Target target, TargetPointer address)
// Address of the exception tracker
ExceptionTracker = address + (ulong)type.Fields[nameof(ExceptionTracker)].Offset;
- UEWatsonBucketTrackerBuckets = target.ReadPointer(address + (ulong)type.Fields[nameof(UEWatsonBucketTrackerBuckets)].Offset);
+ // UEWatsonBucketTrackerBuckets does not exist on certain platforms
+ UEWatsonBucketTrackerBuckets = type.Fields.TryGetValue(nameof(UEWatsonBucketTrackerBuckets), out Target.FieldInfo watsonFieldInfo)
+ ? target.ReadPointer(address + (ulong)watsonFieldInfo.Offset)
+ : TargetPointer.Null;
ThreadLocalDataPtr = target.ReadPointer(address + (ulong)type.Fields[nameof(ThreadLocalDataPtr)].Offset);
}
diff --git a/src/native/managed/cdac/tests/DumpTests/ClrMdDumpHost.cs b/src/native/managed/cdac/tests/DumpTests/ClrMdDumpHost.cs
new file mode 100644
index 00000000000000..5b948f4973afde
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/ClrMdDumpHost.cs
@@ -0,0 +1,103 @@
+// 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 Microsoft.Diagnostics.Runtime;
+
+namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
+
+///
+/// Wraps a ClrMD DataTarget to provide the memory read callback and symbol lookup
+/// needed to create a from a crash dump.
+///
+internal sealed class ClrMdDumpHost : IDisposable
+{
+ private static readonly string[] s_runtimeModuleNames =
+ {
+ "coreclr.dll",
+ "libcoreclr.so",
+ "libcoreclr.dylib",
+ };
+
+ private readonly DataTarget _dataTarget;
+
+ public string DumpPath { get; }
+
+ private ClrMdDumpHost(string dumpPath, DataTarget dataTarget)
+ {
+ DumpPath = dumpPath;
+ _dataTarget = dataTarget;
+ }
+
+ ///
+ /// Open a crash dump and prepare it for cDAC analysis.
+ ///
+ public static ClrMdDumpHost Open(string dumpPath)
+ {
+ DataTarget dataTarget = DataTarget.LoadDump(dumpPath);
+ return new ClrMdDumpHost(dumpPath, dataTarget);
+ }
+
+ ///
+ /// Read memory from the dump at the specified address.
+ /// Returns 0 on success, non-zero on failure.
+ ///
+ public int ReadFromTarget(ulong address, Span buffer)
+ {
+ int bytesRead = _dataTarget.DataReader.Read(address, buffer);
+ return bytesRead == buffer.Length ? 0 : -1;
+ }
+
+ ///
+ /// Get a thread's register context from the dump.
+ /// Returns 0 on success, non-zero on failure.
+ ///
+ public int GetThreadContext(uint threadId, uint contextFlags, Span buffer)
+ {
+ return _dataTarget.DataReader.GetThreadContext(threadId, contextFlags, buffer) ? 0 : -1;
+ }
+
+ ///
+ /// Locate the DotNetRuntimeContractDescriptor symbol address in the dump.
+ /// Uses ClrMD's built-in export resolution which handles PE, ELF, and Mach-O formats.
+ ///
+ public ulong FindContractDescriptorAddress()
+ {
+ foreach (ModuleInfo module in _dataTarget.DataReader.EnumerateModules())
+ {
+ string? fileName = module.FileName;
+ if (fileName is null)
+ continue;
+
+ // Path.GetFileName doesn't handle Windows paths on a Linux/macOS host,
+ // so split on both separators to extract the file name correctly when
+ // analyzing cross-platform dumps.
+ int lastSep = Math.Max(fileName.LastIndexOf('/'), fileName.LastIndexOf('\\'));
+ string name = lastSep >= 0 ? fileName[(lastSep + 1)..] : fileName;
+ if (!IsRuntimeModule(name))
+ continue;
+
+ ulong address = module.GetExportSymbolAddress("DotNetRuntimeContractDescriptor");
+ if (address != 0)
+ return address;
+ }
+
+ throw new InvalidOperationException("Could not find DotNetRuntimeContractDescriptor export in any runtime module in the dump.");
+ }
+
+ private static bool IsRuntimeModule(string fileName)
+ {
+ foreach (string name in s_runtimeModuleNames)
+ {
+ if (fileName.Equals(name, StringComparison.OrdinalIgnoreCase))
+ return true;
+ }
+
+ return false;
+ }
+
+ public void Dispose()
+ {
+ _dataTarget.Dispose();
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/BasicThreads.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/BasicThreads.csproj
new file mode 100644
index 00000000000000..35e3d8428b7cfc
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/BasicThreads.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/Program.cs
new file mode 100644
index 00000000000000..4571e8566a1c29
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/BasicThreads/Program.cs
@@ -0,0 +1,56 @@
+// 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.Threading;
+
+///
+/// Debuggee app for cDAC dump tests.
+/// Spawns threads with known names, ensures they are all alive, then crashes
+/// so a dump is produced for analysis.
+///
+internal static class Program
+{
+ // These constants are referenced by ThreadDumpTests to assert expected values.
+ public const int SpawnedThreadCount = 5;
+ public static readonly string[] ThreadNames = new[]
+ {
+ "cdac-test-thread-0",
+ "cdac-test-thread-1",
+ "cdac-test-thread-2",
+ "cdac-test-thread-3",
+ "cdac-test-thread-4",
+ };
+
+ private static void Main()
+ {
+ // Barrier ensures all threads are alive and named before we crash.
+ // participantCount = SpawnedThreadCount + 1 (main thread)
+ using Barrier barrier = new(SpawnedThreadCount + 1);
+
+ Thread[] threads = new Thread[SpawnedThreadCount];
+ for (int i = 0; i < SpawnedThreadCount; i++)
+ {
+ int index = i;
+ threads[i] = new Thread(() =>
+ {
+ // Signal that this thread is alive and wait for all others.
+ barrier.SignalAndWait();
+
+ // Keep the thread alive until the process crashes.
+ Thread.Sleep(Timeout.Infinite);
+ })
+ {
+ Name = ThreadNames[index],
+ IsBackground = true,
+ };
+ threads[i].Start();
+ }
+
+ // Wait until all spawned threads have reached the barrier.
+ barrier.SignalAndWait();
+
+ // All threads are alive and named. Crash to produce a dump.
+ Environment.FailFast("cDAC dump test: BasicThreads debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.props b/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.props
new file mode 100644
index 00000000000000..58e85258f42834
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.props
@@ -0,0 +1,17 @@
+
+
+
+
+ Exe
+ $(NetCoreAppToolCurrent);net10.0
+ true
+ enable
+ $(ArtifactsBinDir)DumpTests\$(MSBuildProjectName)\$(Configuration)\
+ true
+
+ $(NoWarn);CA1852
+
+ Heap
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.targets b/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.targets
new file mode 100644
index 00000000000000..8c6ba474d40e6c
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.targets
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+ <_DumpTypeOutput Include="$(MSBuildProjectName)" DumpTypes="$(DumpTypes)" />
+
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/GCRoots.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/GCRoots.csproj
new file mode 100644
index 00000000000000..35e3d8428b7cfc
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/GCRoots.csproj
@@ -0,0 +1,2 @@
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/Program.cs
new file mode 100644
index 00000000000000..fabc70ce665962
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/GCRoots/Program.cs
@@ -0,0 +1,48 @@
+// 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.Runtime.InteropServices;
+
+///
+/// Debuggee for cDAC dump tests — exercises the Object and GC contracts.
+/// Pins objects, creates GC handles, then crashes.
+///
+internal static class Program
+{
+ public const int PinnedObjectCount = 5;
+ public const string TestStringValue = "cDAC-GCRoots-test-string";
+
+ private static void Main()
+ {
+ // Allocate objects of various types
+ string testString = TestStringValue;
+ byte[] byteArray = new byte[1024];
+ object boxedInt = 42;
+
+ // Create pinned handles
+ GCHandle[] pinnedHandles = new GCHandle[PinnedObjectCount];
+ byte[][] pinnedArrays = new byte[PinnedObjectCount][];
+
+ for (int i = 0; i < PinnedObjectCount; i++)
+ {
+ pinnedArrays[i] = new byte[64];
+ pinnedHandles[i] = GCHandle.Alloc(pinnedArrays[i], GCHandleType.Pinned);
+ }
+
+ // Create weak and strong handles
+ var weakRef = new WeakReference(new object());
+ var strongHandle = GCHandle.Alloc(testString, GCHandleType.Normal);
+
+ // Keep references alive
+ GC.KeepAlive(testString);
+ GC.KeepAlive(byteArray);
+ GC.KeepAlive(boxedInt);
+ GC.KeepAlive(pinnedHandles);
+ GC.KeepAlive(pinnedArrays);
+ GC.KeepAlive(weakRef);
+ GC.KeepAlive(strongHandle);
+
+ Environment.FailFast("cDAC dump test: GCRoots debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/MultiModule.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/MultiModule.csproj
new file mode 100644
index 00000000000000..bb776824769fe6
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/MultiModule.csproj
@@ -0,0 +1,5 @@
+
+
+ Full
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/Program.cs
new file mode 100644
index 00000000000000..d803f40e8d39ea
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/MultiModule/Program.cs
@@ -0,0 +1,43 @@
+// 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.Reflection;
+using System.Runtime.Loader;
+
+///
+/// Debuggee for cDAC dump tests — exercises the Loader contract.
+/// Loads assemblies from multiple AssemblyLoadContexts then crashes.
+///
+internal static class Program
+{
+ public const int CustomAlcCount = 3;
+
+ private static void Main()
+ {
+ // Create multiple AssemblyLoadContexts and load the runtime assembly in each
+ // to exercise the Loader contract's module enumeration.
+ AssemblyLoadContext[] contexts = new AssemblyLoadContext[CustomAlcCount];
+ Assembly[] loadedAssemblies = new Assembly[CustomAlcCount];
+
+ for (int i = 0; i < CustomAlcCount; i++)
+ {
+ contexts[i] = new AssemblyLoadContext($"cdac-test-alc-{i}", isCollectible: true);
+
+ // Each ALC will have the core assembly visible.
+ // We can also load the current assembly's location in a new context,
+ // but for simplicity, just exercise the ALC infrastructure.
+ loadedAssemblies[i] = contexts[i].LoadFromAssemblyName(typeof(object).Assembly.GetName());
+ }
+
+ // Also load System.Xml to have another module present
+ var xmlAssembly = Assembly.Load("System.Private.Xml");
+
+ // Keep references alive
+ GC.KeepAlive(contexts);
+ GC.KeepAlive(loadedAssemblies);
+ GC.KeepAlive(xmlAssembly);
+
+ Environment.FailFast("cDAC dump test: MultiModule debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/Program.cs
new file mode 100644
index 00000000000000..ea8cd960dc8d88
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/Program.cs
@@ -0,0 +1,42 @@
+// 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.Runtime;
+using System.Runtime.InteropServices;
+
+///
+/// Debuggee for cDAC dump tests — exercises the GC contract in server GC mode.
+/// Allocates objects across heaps, pins some, then crashes.
+///
+internal static class Program
+{
+ private static void Main()
+ {
+ // Verify server GC is enabled
+ if (!GCSettings.IsServerGC)
+ {
+ Console.Error.WriteLine("ERROR: Server GC is not enabled.");
+ Environment.Exit(1);
+ }
+
+ // Allocate objects to populate multiple heaps
+ object[] roots = new object[100];
+ for (int i = 0; i < roots.Length; i++)
+ {
+ roots[i] = new byte[1024 * (i + 1)];
+ }
+
+ // Create pinned handles
+ GCHandle[] pinnedHandles = new GCHandle[10];
+ for (int i = 0; i < pinnedHandles.Length; i++)
+ {
+ pinnedHandles[i] = GCHandle.Alloc(new byte[256], GCHandleType.Pinned);
+ }
+
+ GC.KeepAlive(roots);
+ GC.KeepAlive(pinnedHandles);
+
+ Environment.FailFast("cDAC dump test: ServerGC debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/ServerGC.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/ServerGC.csproj
new file mode 100644
index 00000000000000..e634aa8ad21491
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/ServerGC/ServerGC.csproj
@@ -0,0 +1,5 @@
+
+
+ true
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs
new file mode 100644
index 00000000000000..7fed5894a501b7
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/Program.cs
@@ -0,0 +1,38 @@
+// 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.Runtime.CompilerServices;
+
+///
+/// Debuggee for cDAC dump tests — exercises the StackWalk contract.
+/// Establishes a deep, deterministic call stack then crashes.
+/// Each method uses NoInlining to ensure distinct stack frames.
+///
+internal static class Program
+{
+ public const int ExpectedManagedFrameCount = 4;
+
+ private static void Main()
+ {
+ MethodA();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void MethodA()
+ {
+ MethodB();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void MethodB()
+ {
+ MethodC();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void MethodC()
+ {
+ Environment.FailFast("cDAC dump test: StackWalk debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/StackWalk.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/StackWalk.csproj
new file mode 100644
index 00000000000000..bb776824769fe6
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/StackWalk.csproj
@@ -0,0 +1,5 @@
+
+
+ Full
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/Program.cs b/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/Program.cs
new file mode 100644
index 00000000000000..158b43dccc55c9
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/Program.cs
@@ -0,0 +1,93 @@
+// 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;
+
+///
+/// Debuggee for cDAC dump tests — exercises the RuntimeTypeSystem and Loader contracts.
+/// Loads types with inheritance, generics, and arrays, then crashes.
+///
+internal static class Program
+{
+ // Base class hierarchy
+ public class Animal
+ {
+ public virtual string Name => "Animal";
+ }
+
+ public class Dog : Animal
+ {
+ public override string Name => "Dog";
+ public string Breed { get; set; } = "Unknown";
+ }
+
+ public class GuideDog : Dog
+ {
+ public string Handler { get; set; } = "None";
+ }
+
+ // Generic types
+ public class Container
+ {
+ public T? Value { get; set; }
+ }
+
+ public class Pair
+ {
+ public TKey? Key { get; set; }
+ public TValue? Value { get; set; }
+ }
+
+ // Interface hierarchy
+ public interface IIdentifiable
+ {
+ int Id { get; }
+ }
+
+ public class IdentifiableAnimal : Animal, IIdentifiable
+ {
+ public int Id { get; set; }
+ }
+
+ private static void Main()
+ {
+ // Create instances so the runtime loads and lays out these types
+ var dog = new Dog { Breed = "Labrador" };
+ var guideDog = new GuideDog { Handler = "John", Breed = "Shepherd" };
+ var container = new Container { Value = 42 };
+ var pair = new Pair { Key = "Rex", Value = dog };
+ var idAnimal = new IdentifiableAnimal { Id = 1 };
+
+ // Arrays of various types
+ int[] intArray = new[] { 1, 2, 3, 4, 5 };
+ string[] stringArray = new[] { "hello", "world" };
+ Dog[] dogArray = new[] { dog, guideDog };
+
+ // Multi-dimensional array
+ int[,] matrix = new int[3, 3];
+
+ // Generic collections
+ var list = new List { dog, guideDog, idAnimal };
+ var dict = new Dictionary
+ {
+ ["dog"] = dog,
+ ["guide"] = guideDog,
+ };
+
+ // Keep references alive
+ GC.KeepAlive(dog);
+ GC.KeepAlive(guideDog);
+ GC.KeepAlive(container);
+ GC.KeepAlive(pair);
+ GC.KeepAlive(idAnimal);
+ GC.KeepAlive(intArray);
+ GC.KeepAlive(stringArray);
+ GC.KeepAlive(dogArray);
+ GC.KeepAlive(matrix);
+ GC.KeepAlive(list);
+ GC.KeepAlive(dict);
+
+ Environment.FailFast("cDAC dump test: TypeHierarchy debuggee intentional crash");
+ }
+}
diff --git a/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/TypeHierarchy.csproj b/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/TypeHierarchy.csproj
new file mode 100644
index 00000000000000..bb776824769fe6
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/Debuggees/TypeHierarchy/TypeHierarchy.csproj
@@ -0,0 +1,5 @@
+
+
+ Full
+
+
diff --git a/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs b/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs
new file mode 100644
index 00000000000000..ec01919743f604
--- /dev/null
+++ b/src/native/managed/cdac/tests/DumpTests/DumpTestBase.cs
@@ -0,0 +1,161 @@
+// 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 System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Microsoft.Diagnostics.DataContractReader.Contracts;
+using Microsoft.DotNet.XUnitExtensions;
+using Xunit;
+
+namespace Microsoft.Diagnostics.DataContractReader.DumpTests;
+
+///
+/// Base class for dump-based cDAC integration tests.
+/// Each test is a [ConditionalTheory] parameterized by .
+/// Call at the start of every test method to load
+/// the dump and evaluate skip attributes such as .
+///
+public abstract class DumpTestBase : IDisposable
+{
+ private ClrMdDumpHost? _host;
+ private ContractDescriptorTarget? _target;
+
+ ///
+ /// The set of runtime versions to test against.
+ /// Each entry produces a separate test invocation via [MemberData].
+ ///
+ public static IEnumerable