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
32 changes: 22 additions & 10 deletions eng/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ Param(
[switch] $skipmanaged,
[switch] $skipnative,
[switch] $bundletools,
[switch] $useCdac,
[switch] $noFallback,
[ValidateSet("", "cdac", "cdacfallback", "cdacverify", "dac")][string] $dacMode = '',
[string] $cdacPath = '',
[switch] $testInterpreter,
[string] $methodfilter = '',
[string] $classfilter = '',
Expand All @@ -26,11 +26,12 @@ Param(
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

if ($noFallback -and -not $useCdac) {
Write-Error "-noFallback requires -useCdac to also be specified."
if ($cdacPath -ne '' -and $dacMode -ne 'cdac') {
Write-Error "-cdacPath is only valid with -dacMode cdac."
exit 1
}


$crossbuild = $false
if (($architecture -eq "arm") -or ($architecture -eq "arm64")) {
$processor = @([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())
Expand Down Expand Up @@ -73,6 +74,21 @@ if (-not $skipnative) {
}
}

# Overlay an externally-provided cDAC (mscordaccore_universal) next to the freshly built sos.dll. SOS
# resolves the cDAC from its own native binaries directory, so this is the only spot it is picked up
# from. Used by the cdac DacMode to exercise the runtime-under-test's own cDAC instead of the copy
# restored from a referenced runtime package.
if ($cdacPath -ne '') {
if (-not (Test-Path $cdacPath)) {
Write-Error "-cdacPath '$cdacPath' does not exist."
exit 1
}
$cdacDest = Join-Path (Join-Path $artifactsdir "bin\$os.$architecture.$configuration") "mscordaccore_universal.dll"
New-Item -ItemType Directory -Force -Path (Split-Path $cdacDest -Parent) | Out-Null
Write-Host "Overlaying cDAC: $cdacPath -> $cdacDest"
Copy-Item $cdacPath $cdacDest -Force
}

# Install sdk for building, restore and build managed components.
# Test runtime installation and debuggee building is handled by src/tests/dirs.proj targets.
if (-not $skipmanaged) {
Expand All @@ -90,12 +106,8 @@ if (-not $skipmanaged) {
# Run the xunit tests
if ($test) {
if (-not $crossbuild) {
if ($useCdac) {
$env:SOS_TEST_CDAC="true"
}

if ($noFallback) {
$env:SOS_TEST_CDAC_NO_FALLBACK="true"
if ($dacMode -ne '') {
$env:SOS_TEST_DAC_MODE=$dacMode
}

if ($testInterpreter) {
Expand Down
52 changes: 38 additions & 14 deletions eng/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ __PrivateBuild=0
__Test=0
__TestFilter=
__UnprocessedBuildArgs=
__UseCdac=0
__NoFallback=0
__DacMode=
__CDacPath=
Comment thread
max-charlamb marked this conversation as resolved.
__TestInterpreter=0
__LiveRuntimeDir=

Expand All @@ -40,6 +40,8 @@ usage_list+=("-skipnative: do not build native components.")
usage_list+=("-test: run xunit tests")
usage_list+=("-methodfilter: pass method filter to xunit runner (Namespace.ClassName.MethodName)")
usage_list+=("-classfilter: pass class filter to xunit runner (Namespace.ClassName)")
usage_list+=("-dacmode: which DAC/cDAC the SOS tests load: cdac, cdacfallback, cdacverify, or dac.")
usage_list+=("-cdacpath: path to an mscordaccore_universal to overlay next to sos.dll (only with -dacmode cdac).")

handle_arguments() {
lowerI="$(echo "${1/--/-}" | tr "[:upper:]" "[:lower:]")"
Expand Down Expand Up @@ -115,12 +117,14 @@ handle_arguments() {
__ShiftArgs=1
;;

usecdac|-usecdac)
__UseCdac=1
dacmode|-dacmode)
__DacMode="$(echo "$2" | tr "[:upper:]" "[:lower:]")"
__ShiftArgs=1
;;

nofallback|-nofallback)
__NoFallback=1
cdacpath|-cdacpath)
__CDacPath="$2"
__ShiftArgs=1
;;

testinterpreter|-testinterpreter)
Expand All @@ -140,8 +144,12 @@ handle_arguments() {

source "$__RepoRootDir"/eng/native/build-commons.sh

if [[ "$__NoFallback" == 1 && "$__UseCdac" != 1 ]]; then
echo "-nofallback requires -usecdac to also be specified."
case "$__DacMode" in
""|cdac|cdacfallback|cdacverify|dac) ;;
*) echo "Invalid -dacmode '$__DacMode'. Expected cdac, cdacfallback, cdacverify, or dac."; exit 1 ;;
esac
if [[ -n "$__CDacPath" && "$__DacMode" != "cdac" ]]; then
echo "-cdacpath is only valid with -dacmode cdac."
exit 1
fi

Expand Down Expand Up @@ -234,6 +242,26 @@ if [[ "$__NativeBuild" == 1 ]]; then
fi
fi

#
# Overlay an externally-provided cDAC (libmscordaccore_universal) next to the freshly built sos so
# SOS resolves it from its own native binaries directory. Used by the cdac DacMode to exercise the
# runtime-under-test's own cDAC instead of the copy restored from a referenced runtime package.
#
if [[ -n "$__CDacPath" ]]; then
if [[ ! -f "$__CDacPath" ]]; then
echo "-cdacpath '$__CDacPath' does not exist."
exit 1
fi
if [[ "$__TargetOS" == "osx" ]]; then
__CDacDestName="libmscordaccore_universal.dylib"
else
__CDacDestName="libmscordaccore_universal.so"
fi
mkdir -p "$__BinDir"
echo "Overlaying cDAC: $__CDacPath -> $__BinDir/$__CDacDestName"
cp -f "$__CDacPath" "$__BinDir/$__CDacDestName"
fi

#
# Managed build
#
Expand Down Expand Up @@ -311,12 +339,8 @@ if [[ "$__Test" == 1 ]]; then
# The .NET createdump facility writes dumps directly and is not affected by ulimit.
ulimit -c 0

if [[ "$__UseCdac" == 1 ]]; then
export SOS_TEST_CDAC="true"
fi

if [[ "$__NoFallback" == 1 ]]; then
export SOS_TEST_CDAC_NO_FALLBACK="true"
if [[ -n "$__DacMode" ]]; then
export SOS_TEST_DAC_MODE="$__DacMode"
fi

if [[ "$__TestInterpreter" == 1 ]]; then
Expand Down
2 changes: 1 addition & 1 deletion eng/testsoscdac.cmd
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
set SOS_TEST_CDAC=true
set SOS_TEST_DAC_MODE=cdacfallback
%~dp0..\.dotnet\dotnet.exe test --no-build --logger "console;verbosity=detailed" %~dp0..\src\tests\SOS.UnitTests\SOS.UnitTests.csproj --filter "Category=CDACCompatible"
2 changes: 1 addition & 1 deletion eng/testsoscdac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ done

scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
export LLDB_PATH=/usr/bin/lldb
export SOS_TEST_CDAC=true
export SOS_TEST_DAC_MODE=cdacfallback
$scriptroot/../.dotnet/dotnet test --no-build --logger "console;verbosity=detailed" $scriptroot/../src/tests/SOS.UnitTests/SOS.UnitTests.csproj --filter "Category=CDACCompatible"
83 changes: 64 additions & 19 deletions src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ private void ParseConfigFile(string path)
["TargetRid"] = GetRid(),
["TargetArchitecture"] = OS.TargetArchitecture.ToString().ToLowerInvariant(),
["NuGetPackageCacheDir"] = nugetPackages,
["TestCDAC"] = Environment.GetEnvironmentVariable("SOS_TEST_CDAC"),
["TestCDACNoFallback"] = Environment.GetEnvironmentVariable("SOS_TEST_CDAC_NO_FALLBACK")
};
if (OS.Kind == OSKind.Windows)
{
Expand Down Expand Up @@ -455,13 +453,20 @@ private string GetStringViewWithVersion(string version)
{
sb.Append(".singlefile");
}
if (TestCDACNoFallback)
switch (DacMode)
{
sb.Append(".cdac_no_fallback");
}
else if (TestCDAC)
{
sb.Append(".cdac");
case DacMode.CDacFallback:
sb.Append(".cdacfallback");
break;
case DacMode.CDacVerify:
sb.Append(".cdacverify");
break;
case DacMode.CDac:
sb.Append(".cdac");
break;
case DacMode.Dac:
sb.Append(".dac");
break;
}
if (UseInterpreter)
{
Expand Down Expand Up @@ -562,19 +567,25 @@ public bool IsDesktop
}

/// <summary>
/// Returns true if test should use the cDAC.
/// Controls which DAC/cDAC the SOS tests load (see <see cref="Microsoft.Diagnostics.TestHelpers.DacMode"/>). The value comes
/// from the SOS_TEST_DAC_MODE environment variable, which eng/build.* sets from its -dacMode argument
/// (and which direct "dotnet test" runs can set themselves).
/// </summary>
public bool TestCDAC
public DacMode DacMode
{
get { return string.Equals(GetValue("TestCDAC"), "true", StringComparison.InvariantCultureIgnoreCase); }
}

/// <summary>
/// Returns true if tests should use the cDAC with no fallback to the legacy DAC.
/// </summary>
public bool TestCDACNoFallback
{
get { return string.Equals(GetValue("TestCDACNoFallback"), "true", StringComparison.InvariantCultureIgnoreCase); }
get
{
string mode = Environment.GetEnvironmentVariable("SOS_TEST_DAC_MODE");
return (mode ?? string.Empty).Trim().ToLowerInvariant() switch
{
"" => DacMode.Default,
"cdac" => DacMode.CDac,
"cdacfallback" => DacMode.CDacFallback,
"cdacverify" => DacMode.CDacVerify,
"dac" => DacMode.Dac,
_ => throw new NotSupportedException($"Unknown DacMode '{mode}'. Expected cdac, cdacfallback, cdacverify, dac, or empty."),
};
}
}

/// <summary>
Expand Down Expand Up @@ -952,6 +963,40 @@ public enum OSKind
Unknown,
}

/// <summary>
/// Controls which DAC/cDAC the SOS tests load. The harness (SOSRunner) translates this into the
/// DOTNET_ENABLE_CDAC / CDAC_NO_FALLBACK environment variables and the "runtimes --usecdac" SOS
/// command, so there are no per-mode special cases elsewhere.
/// </summary>
public enum DacMode
{
/// <summary>
/// Unspecified: the harness applies no DAC/cDAC configuration (SOS uses its default load policy).
/// </summary>
Default,

/// <summary>
/// cDAC hosted by the in-box DAC, with per-API fallback to the legacy DAC.
/// </summary>
CDacFallback,

/// <summary>
/// cDAC hosted by the in-box DAC, with no fallback to the legacy DAC (still verifies against it).
/// </summary>
CDacVerify,

/// <summary>
/// The standalone cDAC (mscordaccore_universal) loaded directly through SOS hosting -- the
/// canonical cDAC path.
/// </summary>
CDac,

/// <summary>
/// The legacy in-box DAC only.
/// </summary>
Dac,
}

/// <summary>
/// The OS specific configuration
/// </summary>
Expand Down
49 changes: 41 additions & 8 deletions src/tests/SOS.UnitTests/SOSRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -719,14 +719,30 @@ public static async Task<SOSRunner> StartDebugger(TestInformation information, D
WithLog(scriptLogger).
WithTimeout(TimeSpan.FromMinutes(10));

if (config.TestCDACNoFallback)
// Configure which DAC/cDAC SOS loads, driven entirely by the DacMode test setting (see
// TestConfiguration.DacMode). The harness translates the mode through two channels:
// * Env vars (DOTNET_ENABLE_CDAC / CDAC_NO_FALLBACK) set here on the debugger process. The
// in-box DAC, loaded into the debugger host, reads them and hosts the cDAC reader itself;
// there is no SOS-command equivalent. They are set before the debugger launches.
// * SOS's own cDAC load policy ("runtimes --usecdac"), applied in LoadSosExtension. That
// command is an SOS extension command and is not available until SOS has been loaded, so
// it cannot be issued as a pre-SOS initial debugger command.
switch (config.DacMode)
{
processRunner.WithEnvironmentVariable("DOTNET_ENABLE_CDAC", "1");
processRunner.WithEnvironmentVariable("CDAC_NO_FALLBACK", "1");
}
else if (config.TestCDAC)
{
processRunner.WithEnvironmentVariable("DOTNET_ENABLE_CDAC", "1");
case DacMode.CDacFallback:
// cDAC hosted by the in-box DAC, with per-API fallback to the legacy DAC.
processRunner.WithEnvironmentVariable("DOTNET_ENABLE_CDAC", "1");
break;
case DacMode.CDacVerify:
// cDAC hosted by the in-box DAC, with no fallback to the legacy DAC.
processRunner.WithEnvironmentVariable("DOTNET_ENABLE_CDAC", "1");
processRunner.WithEnvironmentVariable("CDAC_NO_FALLBACK", "1");
break;
case DacMode.CDac:
case DacMode.Dac:
case DacMode.Default:
// No debuggee env vars; the SOS load policy (if any) is applied in LoadSosExtension.
break;
}

// Enable stress logging for both live and dump paths when requested
Expand Down Expand Up @@ -1134,6 +1150,23 @@ public async Task LoadSosExtension()
default:
throw new Exception($"{DebuggerToString} cannot load sos extension");
}

// Apply the cDAC load policy selected by the test's DacMode now that SOS is loaded (the
// "runtimes" command is unavailable before this) and before any runtime is accessed, so SOS
// uses the requested DAC/cDAC the first time it resolves the runtime. CDacFallback/CDacVerify
// instead rely on the in-box DAC via env vars set in StartDebugger and keep SOS's default
// policy (which does not load the standalone cDAC when DOTNET_ENABLE_CDAC is set).
string cdacPolicyCommand = _config.DacMode switch
{
DacMode.CDac => "runtimes --usecdac true", // Force the standalone cDAC next to sos.dll.
DacMode.Dac => "runtimes --usecdac false", // Force the legacy in-box DAC.
_ => null,
};
if (cdacPolicyCommand is not null && Debugger != NativeDebugger.Gdb)
{
commands.Add((Debugger == NativeDebugger.Cdb ? "!" : "") + cdacPolicyCommand);
}

await RunCommands(commands);

// Helper function to switch to the thread with an exception
Expand Down Expand Up @@ -1599,7 +1632,7 @@ private HashSet<string> GetEnabledDefines()
{
defines.Add("HOST_RUNTIME_NONE");
}
if (_config.TestCDACNoFallback)
if (_config.DacMode == DacMode.CDacVerify)
{
defines.Add("CDAC_NO_FALLBACK_TESTING");
}
Expand Down