Skip to content
Closed
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
24 changes: 24 additions & 0 deletions diagnostics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ extends:
architecture: arm64
artifactUploadPath: bin/Windows_NT.arm64.Release

- ${{ if ne(parameters.buildOnly, true) }}:
- template: /eng/pipelines/build.yml
parameters:
jobTemplate: ${{ variables.jobTemplate }}
name: Windows_cDAC
osGroup: Windows_NT
useCdac: true
buildConfigs:
- configuration: Release
architecture: x64

- template: /eng/pipelines/build.yml
parameters:
jobTemplate: ${{ variables.jobTemplate }}
Expand Down Expand Up @@ -234,6 +245,19 @@ extends:
- configuration: Debug
architecture: x64

- template: /eng/pipelines/build.yml
parameters:
jobTemplate: ${{ variables.jobTemplate }}
name: Ubuntu_22_04_cDAC
osGroup: Linux
container: test_ubuntu_22_04
dependsOn: Linux
testOnly: true
useCdac: true
buildConfigs:
- configuration: Release
architecture: x64

- template: /eng/pipelines/build.yml
parameters:
jobTemplate: ${{ variables.jobTemplate }}
Expand Down
18 changes: 18 additions & 0 deletions eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,30 @@ if ($bundletools) {
$test = $False
}

if ($useCdac) {
$remainingargs = "/p:PackageWithCDac=true " + $remainingargs
}

# Build native components
if (-not $skipnative) {
Invoke-Expression "& `"$engroot\Build-Native.cmd`" -architecture $architecture -configuration $configuration -verbosity $verbosity $remainingargs"
if ($lastExitCode -ne 0) {
exit $lastExitCode
}
} elseif ($useCdac) {
# When native build is skipped but cDAC is requested, run native-prereqs to download the cDAC transport package
& "$engroot\common\msbuild.ps1" `
$engroot\native-prereqs.proj `
-verbosity $verbosity `
/t:InstallNativePackages `
/restore `
/bl:$logdir\InstallCdac.binlog `
/p:TargetOS=$os `
/p:TargetArch=$architecture `
/p:PackageWithCDac=true
if ($lastExitCode -ne 0) {
exit $lastExitCode
}
}

# Install sdk for building, restore and build managed components.
Expand Down
24 changes: 24 additions & 0 deletions eng/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ handle_arguments() {

usecdac|-usecdac)
__UseCdac=1
__UnprocessedBuildArgs="$__UnprocessedBuildArgs /p:PackageWithCDac=true"
;;

-warnaserror|-nodereuse)
Expand Down Expand Up @@ -224,6 +225,29 @@ if [[ "$__NativeBuild" == 1 ]]; then
fi
fi

#
# If native build is skipped but cDAC is requested, download the cDAC transport package
#
if [[ "$__NativeBuild" == 0 && "$__UseCdac" == 1 ]]; then
echo "Installing cDAC transport package..."
"$__RepoRootDir/eng/common/msbuild.sh" \
$__RepoRootDir/eng/native-prereqs.proj \
/bl:"$__LogsDir/InstallCdac.binlog" \
/t:InstallNativePackages \
/restore \
/p:Configuration="$__BuildType" \
/p:TargetOS="$__TargetOS" \
/p:TargetArch="$__TargetArch" \
/p:TargetRid="$__TargetRid" \
/p:Platform="$__TargetArch" \
/p:PackageWithCDac=true

if [ $? != 0 ]; then
echo "Installing cDAC transport package FAILED"
exit 1
fi
fi

#
# Managed build
#
Expand Down
11 changes: 11 additions & 0 deletions eng/pipelines/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ parameters:
type: boolean
default: false

# Optional: run tests with cDAC-only mode
- name: useCdac
type: boolean
default: false

# Optional: architecture cross build if true
- name: crossBuild
type: boolean
Expand Down Expand Up @@ -144,6 +149,12 @@ jobs:
- ${{ if eq(parameters.testOnly, 'true') }}:
- _TestArgs: '-test -skipnative'

- ${{ if and(eq(parameters.useCdac, 'true'), eq(parameters.testOnly, 'true')) }}:
- _TestArgs: '-test -skipnative -privatebuild -useCdac'

- ${{ if and(eq(parameters.useCdac, 'true'), ne(parameters.testOnly, 'true')) }}:
- _TestArgs: '-test -privatebuild -useCdac'

- ${{ if or(eq(parameters.buildOnly, 'true'), eq(parameters.isCodeQLRun, 'true')) }}:
- _TestArgs: ''

Expand Down
2 changes: 0 additions & 2 deletions eng/testsoscdac.cmd

This file was deleted.

18 changes: 0 additions & 18 deletions eng/testsoscdac.sh

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,21 @@ public ITarget OpenDump(string fileName)

try
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && (targetPlatform != OSPlatform.OSX))
{
throw new NotSupportedException("Analyzing Windows or Linux dumps not supported when running on MacOS");
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && (targetPlatform != OSPlatform.Linux))
// Cross-platform dump analysis is allowed when using cDAC-only mode (ForceUseContractReader)
// because the cDAC is a host-native NativeAOT binary that can analyze dumps from any platform.
ISettingsService settingsService = _host.Services.GetService<ISettingsService>();
bool allowCrossPlatform = settingsService?.ForceUseContractReader == true;

if (!allowCrossPlatform)
{
throw new NotSupportedException("Analyzing Windows or MacOS dumps not supported when running on Linux");
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && (targetPlatform != OSPlatform.OSX))
{
throw new NotSupportedException("Analyzing Windows or Linux dumps not supported when running on MacOS");
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && (targetPlatform != OSPlatform.Linux))
{
throw new NotSupportedException("Analyzing Windows or MacOS dumps not supported when running on Linux");
}
}
return new TargetFromDataReader(dataTarget, targetPlatform, _host, fileName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ private string GetLibraryPath(DebugLibraryKind kind)

foreach (DebugLibraryInfo libraryInfo in _clrInfo.DebuggingLibraries)
{
if (libraryInfo.Kind == kind && RuntimeInformation.IsOSPlatform(libraryInfo.Platform) && libraryInfo.TargetArchitecture == currentArch)
// For cDAC, skip the platform filter — cDAC is a host-native NativeAOT binary
// that can analyze dumps from any target platform.
bool platformMatch = kind == DebugLibraryKind.CDac || RuntimeInformation.IsOSPlatform(libraryInfo.Platform);

if (libraryInfo.Kind == kind && platformMatch && libraryInfo.TargetArchitecture == currentArch)
{
libraryPath = GetLocalPath(libraryInfo);
if (libraryPath is not null)
Expand All @@ -206,7 +210,39 @@ private string GetLocalPath(DebugLibraryInfo libraryInfo)
string localFilePath;
if (libraryInfo.Kind == DebugLibraryKind.CDac)
{
string fileName = Path.GetFileName(libraryInfo.FileName);

// First try the absolute path from ClrMD (works for same-platform scenarios)
localFilePath = libraryInfo.FileName;
if (File.Exists(localFilePath))
{
return localFilePath;
}
// Try the RID subdirectory alongside the ClrMD-reported path. The dotnet-dump publish
// layout places native binaries under a RID subdirectory (e.g., win-x64/).
localFilePath = TryGetCDacInRidSubdirectory(Path.GetDirectoryName(libraryInfo.FileName), fileName);
if (localFilePath is not null)
{
return localFilePath;
}
// Fall back to RuntimeModuleDirectory if set (supports user-provided cDAC path via setclrpath)
if (!string.IsNullOrEmpty(RuntimeModuleDirectory))
{
localFilePath = Path.Combine(RuntimeModuleDirectory, fileName);
if (File.Exists(localFilePath))
{
return localFilePath;
}
}
// Fall back to the directory containing the runtime module (e.g., coreclr.dll).
// The cDAC may ship alongside the runtime in the shared framework directory.
string runtimeDir = Path.GetDirectoryName(RuntimeModule.FileName);
localFilePath = Path.Combine(runtimeDir, fileName);
if (File.Exists(localFilePath))
{
return localFilePath;
}
return null;
}
else
{
Expand All @@ -226,6 +262,29 @@ private string GetLocalPath(DebugLibraryInfo libraryInfo)
return localFilePath;
}

/// <summary>
/// Searches for the cDAC binary under RID subdirectories (e.g., win-x64/) of the given directory.
/// The dotnet-dump publish layout places native assets in RID-specific subdirectories.
/// </summary>
private static string TryGetCDacInRidSubdirectory(string directory, string fileName)
{
if (string.IsNullOrEmpty(directory))
{
return null;
}
string os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win"
: RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux"
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx"
: null;
if (os is null)
{
return null;
}
string rid = $"{os}-{RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant()}";
string localFilePath = Path.Combine(directory, rid, fileName);
return File.Exists(localFilePath) ? localFilePath : null;
}

private string DownloadFile(DebugLibraryInfo libraryInfo)
{
OSPlatform platform = Target.OperatingSystem;
Expand Down
17 changes: 14 additions & 3 deletions src/SOS/SOS.Hosting/RuntimeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,13 @@ private int GetClrDataProcess(
return HResult.E_INVALIDARG;
}
*ppClrDataProcess = IntPtr.Zero;
if ((flags & ClrDataProcessFlags.UseCDac) != 0)

ISettingsService settingsService = _services.GetService<ISettingsService>();
bool forceUseCDac = settingsService?.ForceUseContractReader == true;

// Try cDAC if explicitly requested via flags, or if ForceUseContractReader is set
// (which overrides the caller's flags — callers like LoadClrDebugDll may not pass UseCDac).
if ((flags & ClrDataProcessFlags.UseCDac) != 0 || forceUseCDac)
{
if (_cdacDataProcess == IntPtr.Zero)
{
Expand All @@ -253,8 +259,9 @@ private int GetClrDataProcess(
}
*ppClrDataProcess = _cdacDataProcess;
}
// Fallback to regular DAC instance if CDac isn't enabled or there where errors creating the instance
if (*ppClrDataProcess == IntPtr.Zero)
// Skip legacy DAC fallback when cDAC-only mode is forced — there may not be a
// platform-matching legacy DAC available (e.g., analyzing a Linux dump on Windows).
if (*ppClrDataProcess == IntPtr.Zero && !forceUseCDac)
{
if (_clrDataProcess == IntPtr.Zero)
{
Expand All @@ -271,6 +278,10 @@ private int GetClrDataProcess(
}
if (*ppClrDataProcess == IntPtr.Zero)
{
if (forceUseCDac)
{
Trace.TraceError("cDAC-only mode (ForceUseContractReader): cDAC failed to load and legacy DAC fallback is disabled.");
}
return HResult.E_NOINTERFACE;
}
return HResult.S_OK;
Expand Down
Loading
Loading