diff --git a/eng/pipelines/common/templates/runtimes/build-test-job.yml b/eng/pipelines/common/templates/runtimes/build-test-job.yml
index b3b62e96256e5c..5faf51570288f4 100644
--- a/eng/pipelines/common/templates/runtimes/build-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/build-test-job.yml
@@ -111,7 +111,7 @@ jobs:
displayName: Disk Usage before Build
# Build managed test components
- - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(logRootNameArg)Managed allTargets skipnative skipgeneratelayout skiptestwrappers $(buildConfig) $(archType) $(runtimeFlavorArgs) $(crossArg) $(priorityArg) $(testFilterArg) ci /p:TargetOS=AnyOS
+ - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(logRootNameArg)Managed allTargets skipnative skipgeneratelayout $(buildConfig) $(archType) $(runtimeFlavorArgs) $(crossArg) $(priorityArg) $(testFilterArg) ci /p:TargetOS=AnyOS
displayName: Build managed test components
- ${{ if in(parameters.osGroup, 'osx', 'ios', 'tvos') }}:
diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml
index 44a5d48a7b478e..b521530b76836a 100644
--- a/eng/pipelines/common/templates/runtimes/run-test-job.yml
+++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml
@@ -236,12 +236,6 @@ jobs:
- script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) copynativeonly $(logRootNameArg)Native $(testFilterArg) $(runtimeFlavorArgs) $(crossgenArg) $(buildConfig) $(archType) $(priorityArg) $(librariesOverrideArg) $(codeFlowEnforcementArg)
displayName: Copy native test components to test output folder
-
- # Generate test wrappers. This is the step that examines issues.targets to exclude tests.
- - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) buildtestwrappersonly $(logRootNameArg)Wrappers $(runtimeFlavorArgs) $(crossgenArg) $(buildConfig) $(archType) $(crossArg) $(priorityArg) $(librariesOverrideArg) $(runtimeVariantArg)
- displayName: Generate test wrappers
-
-
# Compose the Core_Root folder containing all artifacts needed for running
# CoreCLR tests. This step also compiles the framework using Crossgen2
# in ReadyToRun jobs.
diff --git a/src/tests/Common/CLRTest.CrossGen.targets b/src/tests/Common/CLRTest.CrossGen.targets
index 6fdd2000655c7e..8b45eb30ddec36 100644
--- a/src/tests/Common/CLRTest.CrossGen.targets
+++ b/src/tests/Common/CLRTest.CrossGen.targets
@@ -61,8 +61,6 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then
mkdir IL-CG2
cp ./*.dll IL-CG2/
rm IL-CG2/composite-r2r.* 2>/dev/null
- rm IL-CG2/Coreclr.TestWrapper.dll 2>/dev/null
- rm IL-CG2/*.XUnitWrapper.dll 2>/dev/null
if [ ! -z ${CompositeBuildMode+x} ]%3B then
# HACK: copy native shared shim libraries. Not needed on Windows.
@@ -254,8 +252,6 @@ if defined RunCrossGen2 (
mkdir IL-CG2
copy *.dll IL-CG2\
del IL-CG2\composite-r2r.* 2>nul
- del IL-CG2\Coreclr.TestWrapper.dll 2>nul
- del IL-CG2\*.XUnitWrapper.dll 2>nul
if defined CompositeBuildMode (
set ExtraCrossGen2Args=!ExtraCrossGen2Args! --composite
diff --git a/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj b/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
index 6bbebee74d7177..e3232266bc90af 100644
--- a/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
+++ b/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
@@ -7,6 +7,7 @@
+
@@ -19,8 +20,6 @@
-
-
diff --git a/src/tests/Common/CoreCLRTestLibrary/CoreclrTestWrapperLib.cs b/src/tests/Common/CoreCLRTestLibrary/CoreclrTestWrapperLib.cs
new file mode 100644
index 00000000000000..d09282fc976844
--- /dev/null
+++ b/src/tests/Common/CoreCLRTestLibrary/CoreclrTestWrapperLib.cs
@@ -0,0 +1,579 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+
+namespace TestLibrary
+{
+ static class DbgHelp
+ {
+ public enum MiniDumpType : int
+ {
+ MiniDumpNormal = 0x00000000,
+ MiniDumpWithDataSegs = 0x00000001,
+ MiniDumpWithFullMemory = 0x00000002,
+ MiniDumpWithHandleData = 0x00000004,
+ MiniDumpFilterMemory = 0x00000008,
+ MiniDumpScanMemory = 0x00000010,
+ MiniDumpWithUnloadedModules = 0x00000020,
+ MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
+ MiniDumpFilterModulePaths = 0x00000080,
+ MiniDumpWithProcessThreadData = 0x00000100,
+ MiniDumpWithPrivateReadWriteMemory = 0x00000200,
+ MiniDumpWithoutOptionalData = 0x00000400,
+ MiniDumpWithFullMemoryInfo = 0x00000800,
+ MiniDumpWithThreadInfo = 0x00001000,
+ MiniDumpWithCodeSegs = 0x00002000,
+ MiniDumpWithoutAuxiliaryState = 0x00004000,
+ MiniDumpWithFullAuxiliaryState = 0x00008000,
+ MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
+ MiniDumpIgnoreInaccessibleMemory = 0x00020000,
+ MiniDumpWithTokenInformation = 0x00040000,
+ MiniDumpWithModuleHeaders = 0x00080000,
+ MiniDumpFilterTriage = 0x00100000,
+ MiniDumpValidTypeFlags = 0x001fffff
+ }
+
+ [DllImport("DbgHelp.dll", SetLastError = true)]
+ public static extern bool MiniDumpWriteDump(IntPtr handle, int processId, SafeFileHandle file, MiniDumpType dumpType, IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callbackParam);
+ }
+
+ static class Kernel32
+ {
+ public const int MAX_PATH = 260;
+ public const int ERROR_NO_MORE_FILES = 0x12;
+ public const long INVALID_HANDLE = -1;
+
+ public enum Toolhelp32Flags : uint
+ {
+ TH32CS_INHERIT = 0x80000000,
+ TH32CS_SNAPHEAPLIST = 0x00000001,
+ TH32CS_SNAPMODULE = 0x00000008,
+ TH32CS_SNAPMODULE32 = 0x00000010,
+ TH32CS_SNAPPROCESS = 0x00000002,
+ TH32CS_SNAPTHREAD = 0x00000004
+ };
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public unsafe struct ProcessEntry32W
+ {
+ public int Size;
+ public int Usage;
+ public int ProcessID;
+ public IntPtr DefaultHeapID;
+ public int ModuleID;
+ public int Threads;
+ public int ParentProcessID;
+ public int PriClassBase;
+ public int Flags;
+ public fixed char ExeFile[MAX_PATH];
+ }
+
+ [DllImport("kernel32.dll")]
+ public static extern bool CloseHandle(IntPtr handle);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr CreateToolhelp32Snapshot(Toolhelp32Flags flags, int processId);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool Process32FirstW(IntPtr snapshot, ref ProcessEntry32W entry);
+
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern bool Process32NextW(IntPtr snapshot, ref ProcessEntry32W entry);
+ }
+
+ static class @libproc
+ {
+ [DllImport(nameof(libproc))]
+ private static extern int proc_listchildpids(int ppid, int[]? buffer, int byteSize);
+
+ public static unsafe bool ListChildPids(int ppid, out int[] buffer)
+ {
+ int n = proc_listchildpids(ppid, null, 0);
+ buffer = new int[n];
+ return proc_listchildpids(ppid, buffer, buffer.Length * sizeof(int)) != -1;
+ }
+ }
+
+ internal static class ProcessExtensions
+ {
+ public static bool TryGetProcessId(this Process process, out int processId)
+ {
+ try
+ {
+ processId = process.Id;
+ return true;
+ }
+ catch
+ {
+ // Process exited
+ processId = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetProcessName(this Process process, out string processName)
+ {
+ try
+ {
+ processName = process.ProcessName;
+ return true;
+ }
+ catch
+ {
+ // Process exited
+ processName = default;
+ return false;
+ }
+ }
+
+ public unsafe static IEnumerable GetChildren(this Process process)
+ {
+ var children = new List();
+ if (OperatingSystem.IsWindows())
+ {
+ return Windows_GetChildren(process);
+ }
+ else if (OperatingSystem.IsLinux())
+ {
+ return Linux_GetChildren(process);
+ }
+ else if (OperatingSystem.IsMacOS())
+ {
+ return MacOS_GetChildren(process);
+ }
+ return children;
+ }
+
+ private unsafe static IEnumerable Windows_GetChildren(Process process)
+ {
+ var children = new List();
+ IntPtr snapshot = Kernel32.CreateToolhelp32Snapshot(Kernel32.Toolhelp32Flags.TH32CS_SNAPPROCESS, 0);
+ if (snapshot != IntPtr.Zero && snapshot.ToInt64() != Kernel32.INVALID_HANDLE)
+ {
+ try
+ {
+ children = new List();
+ int ppid = process.Id;
+
+ var processEntry = new Kernel32.ProcessEntry32W { Size = sizeof(Kernel32.ProcessEntry32W) };
+
+ bool success = Kernel32.Process32FirstW(snapshot, ref processEntry);
+ while (success)
+ {
+ if (processEntry.ParentProcessID == ppid)
+ {
+ try
+ {
+ children.Add(Process.GetProcessById(processEntry.ProcessID));
+ }
+ catch {}
+ }
+
+ success = Kernel32.Process32NextW(snapshot, ref processEntry);
+ }
+
+ }
+ finally
+ {
+ Kernel32.CloseHandle(snapshot);
+ }
+ }
+
+ return children;
+ }
+
+ private static IEnumerable Linux_GetChildren(Process process)
+ {
+ var children = new List();
+ List? childPids = null;
+
+ try
+ {
+ childPids = File.ReadAllText($"/proc/{process.Id}/task/{process.Id}/children")
+ .Split(' ', StringSplitOptions.RemoveEmptyEntries)
+ .Select(pidString => int.Parse(pidString))
+ .ToList();
+ }
+ catch (IOException e)
+ {
+ // Some distros might not have the /proc/pid/task/tid/children entry enabled in the kernel
+ // attempt to use pgrep then
+ var pgrepInfo = new ProcessStartInfo("pgrep");
+ pgrepInfo.RedirectStandardOutput = true;
+ pgrepInfo.Arguments = $"-P {process.Id}";
+
+ using Process pgrep = Process.Start(pgrepInfo)!;
+
+ string[] pidStrings = pgrep.StandardOutput.ReadToEnd().Split('\n', StringSplitOptions.RemoveEmptyEntries);
+ pgrep.WaitForExit();
+
+ childPids = new List();
+ foreach (var pidString in pidStrings)
+ if (int.TryParse(pidString, out int childPid))
+ childPids.Add(childPid);
+ }
+
+ foreach (var pid in childPids)
+ {
+ try
+ {
+ children.Add(Process.GetProcessById(pid));
+ }
+ catch (ArgumentException)
+ {
+ // Ignore failure to get process, the process may have exited
+ }
+ }
+
+ return children;
+ }
+
+ private static IEnumerable MacOS_GetChildren(Process process)
+ {
+ var children = new List();
+ if (libproc.ListChildPids(process.Id, out int[] childPids))
+ {
+ foreach (var childPid in childPids)
+ {
+ children.Add(Process.GetProcessById(childPid));
+ }
+ }
+
+ return children;
+ }
+ }
+
+ internal class CoreclrTestWrapperLib
+ {
+ public const int EXIT_SUCCESS_CODE = 0;
+ public const string TIMEOUT_ENVIRONMENT_VAR = "__TestTimeout";
+
+ // Default timeout set to 10 minutes
+ public const int DEFAULT_TIMEOUT_MS = 1000 * 60 * 10;
+
+ public const string COLLECT_DUMPS_ENVIRONMENT_VAR = "__CollectDumps";
+ public const string CRASH_DUMP_FOLDER_ENVIRONMENT_VAR = "__CrashDumpFolder";
+
+ public const string TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR = "__TestArchitecture";
+
+ static bool CollectCrashDump(Process process, string crashDumpPath, StreamWriter outputWriter)
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ return CollectCrashDumpWithMiniDumpWriteDump(process, crashDumpPath, outputWriter);
+ }
+ else
+ {
+ return CollectCrashDumpWithCreateDump(process, crashDumpPath, outputWriter);
+ }
+ }
+
+ static bool CollectCrashDumpWithMiniDumpWriteDump(Process process, string crashDumpPath, StreamWriter outputWriter)
+ {
+ bool collectedDump = false;
+ using (var crashDump = File.OpenWrite(crashDumpPath))
+ {
+ var flags = DbgHelp.MiniDumpType.MiniDumpWithFullMemory | DbgHelp.MiniDumpType.MiniDumpIgnoreInaccessibleMemory;
+ collectedDump = DbgHelp.MiniDumpWriteDump(process.Handle, process.Id, crashDump.SafeFileHandle, flags, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+ }
+ return collectedDump;
+ }
+
+ static bool CollectCrashDumpWithCreateDump(Process process, string crashDumpPath, StreamWriter outputWriter)
+ {
+ string? coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT");
+ if (coreRoot is null)
+ {
+ throw new InvalidOperationException("CORE_ROOT environment variable is not set.");
+ }
+ string createdumpPath = Path.Combine(coreRoot, "createdump");
+ string arguments = $"--crashreport --name \"{crashDumpPath}\" {process.Id} --withheap";
+ Process createdump = new Process();
+
+ createdump.StartInfo.FileName = "sudo";
+ createdump.StartInfo.Arguments = $"{createdumpPath} {arguments}";
+
+ createdump.StartInfo.UseShellExecute = false;
+ createdump.StartInfo.RedirectStandardOutput = true;
+ createdump.StartInfo.RedirectStandardError = true;
+
+ Console.WriteLine($"Invoking: {createdump.StartInfo.FileName} {createdump.StartInfo.Arguments}");
+ createdump.Start();
+
+ Task copyOutput = createdump.StandardOutput.ReadToEndAsync();
+ Task copyError = createdump.StandardError.ReadToEndAsync();
+ bool fSuccess = createdump.WaitForExit(DEFAULT_TIMEOUT_MS);
+
+ if (fSuccess)
+ {
+ Task.WaitAll(copyError, copyOutput);
+ string output = copyOutput.Result;
+ string error = copyError.Result;
+
+ Console.WriteLine("createdump stdout:");
+ Console.WriteLine(output);
+ Console.WriteLine("createdump stderr:");
+ Console.WriteLine(error);
+
+ // Ensure the dump is accessible by current user
+ Process chown = new Process();
+ chown.StartInfo.FileName = "sudo";
+ chown.StartInfo.Arguments = $"chown \"{Environment.UserName}\" \"{crashDumpPath}\"";
+
+ chown.StartInfo.UseShellExecute = false;
+ chown.StartInfo.RedirectStandardOutput = true;
+ chown.StartInfo.RedirectStandardError = true;
+
+ Console.WriteLine($"Invoking: {chown.StartInfo.FileName} {chown.StartInfo.Arguments}");
+ chown.Start();
+ copyOutput = chown.StandardOutput.ReadToEndAsync();
+ copyError = chown.StandardError.ReadToEndAsync();
+
+ chown.WaitForExit(DEFAULT_TIMEOUT_MS);
+
+ Task.WaitAll(copyError, copyOutput);
+ Console.WriteLine("chown stdout:");
+ Console.WriteLine(copyOutput.Result);
+ Console.WriteLine("chown stderr:");
+ Console.WriteLine(copyError.Result);
+ }
+ else
+ {
+ // Workaround for https://github.com/dotnet/runtime/issues/93321
+ const int MaxRetries = 5;
+ for (int i = 0; i < MaxRetries; i++)
+ {
+ try
+ {
+ createdump.Kill(entireProcessTree: true);
+ break;
+ }
+ catch (Exception e) when (i < MaxRetries - 1)
+ {
+ Console.WriteLine($"Process.Kill(entireProcessTree: true) failed:");
+ Console.WriteLine(e);
+ Console.WriteLine("Retrying...");
+ }
+ }
+ }
+
+ return fSuccess && createdump.ExitCode == 0;
+ }
+
+ // Finds all children processes starting with a process named childName
+ // The children are sorted in the order they should be dumped
+ static unsafe IEnumerable FindChildProcessesByName(Process process, string childName)
+ {
+ process.TryGetProcessName(out string parentProcessName);
+ process.TryGetProcessId(out int parentProcessId);
+ Console.WriteLine($"Finding all child processes of '{parentProcessName}' (ID: {parentProcessId}) with name '{childName}'");
+
+ var children = new Stack();
+ Queue childrenToCheck = new Queue();
+ HashSet seen = new HashSet();
+
+ seen.Add(parentProcessId);
+
+ try
+ {
+ foreach (var child in process.GetChildren())
+ childrenToCheck.Enqueue(child);
+ }
+ catch
+ {
+ // Process exited
+ }
+
+ while (childrenToCheck.Count != 0)
+ {
+ Process child = childrenToCheck.Dequeue();
+
+ if (!child.TryGetProcessId(out int processId))
+ continue;
+
+ if (seen.Contains(processId))
+ continue;
+
+ if (!child.TryGetProcessName(out string processName))
+ continue;
+
+ Console.WriteLine($"Checking child process: '{processName}' (ID: {processId})");
+ seen.Add(processId);
+
+ try
+ {
+ foreach (var grandchild in child.GetChildren())
+ childrenToCheck.Enqueue(grandchild);
+ }
+ catch
+ {
+ // Process exited
+ }
+
+ if (processName.Equals(childName, StringComparison.OrdinalIgnoreCase))
+ {
+ children.Push(child);
+ }
+ }
+
+ return children;
+ }
+
+ public int RunTest(string executable, string outputFile, string errorFile, string category, string testBinaryBase, string outputDir)
+ {
+ Debug.Assert(outputFile != errorFile);
+
+ int exitCode = -100;
+
+ // If a timeout was given to us by an environment variable, use it instead of the default
+ // timeout.
+ string? environmentVar = Environment.GetEnvironmentVariable(TIMEOUT_ENVIRONMENT_VAR);
+ int timeout = environmentVar != null ? int.Parse(environmentVar) : DEFAULT_TIMEOUT_MS;
+ bool collectCrashDumps = Environment.GetEnvironmentVariable(COLLECT_DUMPS_ENVIRONMENT_VAR) != null;
+ string? crashDumpFolder = Environment.GetEnvironmentVariable(CRASH_DUMP_FOLDER_ENVIRONMENT_VAR);
+
+ var outputStream = new FileStream(outputFile, FileMode.Create);
+ var errorStream = new FileStream(errorFile, FileMode.Create);
+
+ using (var outputWriter = new StreamWriter(outputStream))
+ using (var errorWriter = new StreamWriter(errorStream))
+ using (Process process = new Process())
+ {
+ // Windows can run the executable implicitly
+ if (OperatingSystem.IsWindows())
+ {
+ process.StartInfo.FileName = executable;
+ }
+ // Non-windows needs to be told explicitly to run through /bin/bash shell
+ else
+ {
+ process.StartInfo.FileName = "/bin/bash";
+ process.StartInfo.Arguments = executable;
+ }
+
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.EnvironmentVariables.Add("__Category", category);
+ process.StartInfo.EnvironmentVariables.Add("__TestBinaryBase", testBinaryBase);
+ process.StartInfo.EnvironmentVariables.Add("__OutputDir", outputDir);
+
+ DateTime startTime = DateTime.Now;
+ process.Start();
+
+ var cts = new CancellationTokenSource();
+ Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream, 4096, cts.Token);
+ Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream, 4096, cts.Token);
+
+ if (process.WaitForExit(timeout))
+ {
+ // Process completed. Check process.ExitCode here.
+ exitCode = process.ExitCode;
+ Task.WaitAll(copyOutput, copyError);
+ }
+ else
+ {
+ // Timed out.
+ DateTime endTime = DateTime.Now;
+
+ try
+ {
+ cts.Cancel();
+ }
+ catch { }
+
+ outputWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}{2}{3}, start: {4}, end: {5})",
+ executable, timeout, (environmentVar != null) ? " from variable " : "", (environmentVar != null) ? TIMEOUT_ENVIRONMENT_VAR : "",
+ startTime.ToString(), endTime.ToString());
+ outputWriter.Flush();
+ errorWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}{2}{3}, start: {4}, end: {5})",
+ executable, timeout, (environmentVar != null) ? " from variable " : "", (environmentVar != null) ? TIMEOUT_ENVIRONMENT_VAR : "",
+ startTime.ToString(), endTime.ToString());
+ errorWriter.Flush();
+
+ Console.WriteLine("Collecting diagnostic information...");
+ Console.WriteLine("Snapshot of processes currently running:");
+ Console.WriteLine($"\t{"ID",-6} ProcessName");
+ foreach (var activeProcess in Process.GetProcesses())
+ {
+ activeProcess.TryGetProcessName(out string activeProcessName);
+ activeProcess.TryGetProcessId(out int activeProcessId);
+ Console.WriteLine($"\t{activeProcessId,-6} {activeProcessName}");
+ }
+
+ if (OperatingSystem.IsWindows())
+ {
+ Console.WriteLine("Snapshot of processes currently running (using wmic):");
+ Console.WriteLine(GetAllProcessNames_wmic());
+ }
+
+ if (collectCrashDumps)
+ {
+ if (crashDumpFolder != null)
+ {
+ foreach (var child in FindChildProcessesByName(process, "corerun"))
+ {
+ string crashDumpPath = Path.Combine(Path.GetFullPath(crashDumpFolder), string.Format("crashdump_{0}.dmp", child.Id));
+ Console.WriteLine($"Attempting to collect crash dump: {crashDumpPath}");
+ if (CollectCrashDump(child, crashDumpPath, outputWriter))
+ {
+ Console.WriteLine("Collected crash dump: {0}", crashDumpPath);
+ }
+ else
+ {
+ Console.WriteLine("Failed to collect crash dump");
+ }
+ }
+ }
+ }
+
+ // kill the timed out processes after we've collected dumps
+ process.Kill(entireProcessTree: true);
+ }
+
+ outputWriter.WriteLine("Test Harness Exitcode is : " + exitCode.ToString());
+ outputWriter.Flush();
+ errorWriter.Flush();
+ }
+
+ return exitCode;
+ }
+
+ private static string GetAllProcessNames_wmic()
+ {
+ // The command to execute
+ string command = "wmic process get Name, ProcessId, ParentProcessId";
+
+ // Start the process and capture the output
+ Process process = new Process();
+ process.StartInfo.FileName = "cmd.exe";
+ process.StartInfo.Arguments = $"/c {command}";
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+
+ // Start the process and read the output
+ process.Start();
+ string output = process.StandardOutput.ReadToEnd();
+ process.WaitForExit(100); // wait for 100 ms
+
+ // Output the result
+ return output;
+ }
+ }
+}
diff --git a/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs
index 2084bd02d96607..e08088951f19bd 100644
--- a/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs
+++ b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs
@@ -10,7 +10,6 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
-using CoreclrTestLib;
using Xunit;
namespace TestLibrary
diff --git a/src/tests/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj b/src/tests/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj
deleted file mode 100644
index 30d98c3c9c0fef..00000000000000
--- a/src/tests/Common/Coreclr.TestWrapper/Coreclr.TestWrapper.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- true
- $(NetCoreAppCurrentToolTargetFrameworkMoniker)
- $(NetCoreAppToolCurrent)
- enable
- true
- true
- true
-
-
-
-
-
-
-
-
diff --git a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
deleted file mode 100644
index 16320ca83be53c..00000000000000
--- a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
+++ /dev/null
@@ -1,960 +0,0 @@
-// 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.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-using System.Text.Json.Serialization;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Win32.SafeHandles;
-
-namespace CoreclrTestLib
-{
- static class DbgHelp
- {
- public enum MiniDumpType : int
- {
- MiniDumpNormal = 0x00000000,
- MiniDumpWithDataSegs = 0x00000001,
- MiniDumpWithFullMemory = 0x00000002,
- MiniDumpWithHandleData = 0x00000004,
- MiniDumpFilterMemory = 0x00000008,
- MiniDumpScanMemory = 0x00000010,
- MiniDumpWithUnloadedModules = 0x00000020,
- MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
- MiniDumpFilterModulePaths = 0x00000080,
- MiniDumpWithProcessThreadData = 0x00000100,
- MiniDumpWithPrivateReadWriteMemory = 0x00000200,
- MiniDumpWithoutOptionalData = 0x00000400,
- MiniDumpWithFullMemoryInfo = 0x00000800,
- MiniDumpWithThreadInfo = 0x00001000,
- MiniDumpWithCodeSegs = 0x00002000,
- MiniDumpWithoutAuxiliaryState = 0x00004000,
- MiniDumpWithFullAuxiliaryState = 0x00008000,
- MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
- MiniDumpIgnoreInaccessibleMemory = 0x00020000,
- MiniDumpWithTokenInformation = 0x00040000,
- MiniDumpWithModuleHeaders = 0x00080000,
- MiniDumpFilterTriage = 0x00100000,
- MiniDumpValidTypeFlags = 0x001fffff
- }
-
- [DllImport("DbgHelp.dll", SetLastError = true)]
- public static extern bool MiniDumpWriteDump(IntPtr handle, int processId, SafeFileHandle file, MiniDumpType dumpType, IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callbackParam);
- }
-
- static class Kernel32
- {
- public const int MAX_PATH = 260;
- public const int ERROR_NO_MORE_FILES = 0x12;
- public const long INVALID_HANDLE = -1;
-
- public enum Toolhelp32Flags : uint
- {
- TH32CS_INHERIT = 0x80000000,
- TH32CS_SNAPHEAPLIST = 0x00000001,
- TH32CS_SNAPMODULE = 0x00000008,
- TH32CS_SNAPMODULE32 = 0x00000010,
- TH32CS_SNAPPROCESS = 0x00000002,
- TH32CS_SNAPTHREAD = 0x00000004
- };
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public unsafe struct ProcessEntry32W
- {
- public int Size;
- public int Usage;
- public int ProcessID;
- public IntPtr DefaultHeapID;
- public int ModuleID;
- public int Threads;
- public int ParentProcessID;
- public int PriClassBase;
- public int Flags;
- public fixed char ExeFile[MAX_PATH];
- }
-
- [DllImport("kernel32.dll")]
- public static extern bool CloseHandle(IntPtr handle);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern IntPtr CreateToolhelp32Snapshot(Toolhelp32Flags flags, int processId);
-
- [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- public static extern bool Process32FirstW(IntPtr snapshot, ref ProcessEntry32W entry);
-
- [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- public static extern bool Process32NextW(IntPtr snapshot, ref ProcessEntry32W entry);
- }
-
- static class @libproc
- {
- [DllImport(nameof(libproc))]
- private static extern int proc_listchildpids(int ppid, int[]? buffer, int byteSize);
-
- public static unsafe bool ListChildPids(int ppid, out int[] buffer)
- {
- int n = proc_listchildpids(ppid, null, 0);
- buffer = new int[n];
- return proc_listchildpids(ppid, buffer, buffer.Length * sizeof(int)) != -1;
- }
- }
-
- internal static class ProcessExtensions
- {
- public static bool TryGetProcessId(this Process process, out int processId)
- {
- try
- {
- processId = process.Id;
- return true;
- }
- catch
- {
- // Process exited
- processId = default;
- return false;
- }
- }
-
- public static bool TryGetProcessName(this Process process, out string processName)
- {
- try
- {
- processName = process.ProcessName;
- return true;
- }
- catch
- {
- // Process exited
- processName = default;
- return false;
- }
- }
-
- public unsafe static IEnumerable GetChildren(this Process process)
- {
- var children = new List();
- if (OperatingSystem.IsWindows())
- {
- return Windows_GetChildren(process);
- }
- else if (OperatingSystem.IsLinux())
- {
- return Linux_GetChildren(process);
- }
- else if (OperatingSystem.IsMacOS())
- {
- return MacOS_GetChildren(process);
- }
- return children;
- }
-
- private unsafe static IEnumerable Windows_GetChildren(Process process)
- {
- var children = new List();
- IntPtr snapshot = Kernel32.CreateToolhelp32Snapshot(Kernel32.Toolhelp32Flags.TH32CS_SNAPPROCESS, 0);
- if (snapshot != IntPtr.Zero && snapshot.ToInt64() != Kernel32.INVALID_HANDLE)
- {
- try
- {
- children = new List();
- int ppid = process.Id;
-
- var processEntry = new Kernel32.ProcessEntry32W { Size = sizeof(Kernel32.ProcessEntry32W) };
-
- bool success = Kernel32.Process32FirstW(snapshot, ref processEntry);
- while (success)
- {
- if (processEntry.ParentProcessID == ppid)
- {
- try
- {
- children.Add(Process.GetProcessById(processEntry.ProcessID));
- }
- catch {}
- }
-
- success = Kernel32.Process32NextW(snapshot, ref processEntry);
- }
-
- }
- finally
- {
- Kernel32.CloseHandle(snapshot);
- }
- }
-
- return children;
- }
-
- private static IEnumerable Linux_GetChildren(Process process)
- {
- var children = new List();
- List? childPids = null;
-
- try
- {
- childPids = File.ReadAllText($"/proc/{process.Id}/task/{process.Id}/children")
- .Split(' ', StringSplitOptions.RemoveEmptyEntries)
- .Select(pidString => int.Parse(pidString))
- .ToList();
- }
- catch (IOException e)
- {
- // Some distros might not have the /proc/pid/task/tid/children entry enabled in the kernel
- // attempt to use pgrep then
- var pgrepInfo = new ProcessStartInfo("pgrep");
- pgrepInfo.RedirectStandardOutput = true;
- pgrepInfo.Arguments = $"-P {process.Id}";
-
- using Process pgrep = Process.Start(pgrepInfo)!;
-
- string[] pidStrings = pgrep.StandardOutput.ReadToEnd().Split('\n', StringSplitOptions.RemoveEmptyEntries);
- pgrep.WaitForExit();
-
- childPids = new List();
- foreach (var pidString in pidStrings)
- if (int.TryParse(pidString, out int childPid))
- childPids.Add(childPid);
- }
-
- foreach (var pid in childPids)
- {
- try
- {
- children.Add(Process.GetProcessById(pid));
- }
- catch (ArgumentException)
- {
- // Ignore failure to get process, the process may have exited
- }
- }
-
- return children;
- }
-
- private static IEnumerable MacOS_GetChildren(Process process)
- {
- var children = new List();
- if (libproc.ListChildPids(process.Id, out int[] childPids))
- {
- foreach (var childPid in childPids)
- {
- children.Add(Process.GetProcessById(childPid));
- }
- }
-
- return children;
- }
- }
-
- public class CoreclrTestWrapperLib
- {
- public const int EXIT_SUCCESS_CODE = 0;
- public const string TIMEOUT_ENVIRONMENT_VAR = "__TestTimeout";
-
- // Default timeout set to 10 minutes
- public const int DEFAULT_TIMEOUT_MS = 1000 * 60 * 10;
-
- public const string COLLECT_DUMPS_ENVIRONMENT_VAR = "__CollectDumps";
- public const string CRASH_DUMP_FOLDER_ENVIRONMENT_VAR = "__CrashDumpFolder";
-
- public const string TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR = "__TestArchitecture";
-
- static bool CollectCrashDump(Process process, string crashDumpPath, StreamWriter outputWriter)
- {
- if (OperatingSystem.IsWindows())
- {
- return CollectCrashDumpWithMiniDumpWriteDump(process, crashDumpPath, outputWriter);
- }
- else
- {
- return CollectCrashDumpWithCreateDump(process, crashDumpPath, outputWriter);
- }
- }
-
- static bool CollectCrashDumpWithMiniDumpWriteDump(Process process, string crashDumpPath, StreamWriter outputWriter)
- {
- bool collectedDump = false;
- using (var crashDump = File.OpenWrite(crashDumpPath))
- {
- var flags = DbgHelp.MiniDumpType.MiniDumpWithFullMemory | DbgHelp.MiniDumpType.MiniDumpIgnoreInaccessibleMemory;
- collectedDump = DbgHelp.MiniDumpWriteDump(process.Handle, process.Id, crashDump.SafeFileHandle, flags, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
- }
- if (collectedDump)
- {
- TryPrintStackTraceFromWindowsDmp(crashDumpPath, outputWriter);
- }
- return collectedDump;
- }
-
- static bool CollectCrashDumpWithCreateDump(Process process, string crashDumpPath, StreamWriter outputWriter)
- {
- string? coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT");
- if (coreRoot is null)
- {
- throw new InvalidOperationException("CORE_ROOT environment variable is not set.");
- }
- string createdumpPath = Path.Combine(coreRoot, "createdump");
- string arguments = $"--crashreport --name \"{crashDumpPath}\" {process.Id} --withheap";
- Process createdump = new Process();
-
- createdump.StartInfo.FileName = "sudo";
- createdump.StartInfo.Arguments = $"{createdumpPath} {arguments}";
-
- createdump.StartInfo.UseShellExecute = false;
- createdump.StartInfo.RedirectStandardOutput = true;
- createdump.StartInfo.RedirectStandardError = true;
-
- Console.WriteLine($"Invoking: {createdump.StartInfo.FileName} {createdump.StartInfo.Arguments}");
- createdump.Start();
-
- Task copyOutput = createdump.StandardOutput.ReadToEndAsync();
- Task copyError = createdump.StandardError.ReadToEndAsync();
- bool fSuccess = createdump.WaitForExit(DEFAULT_TIMEOUT_MS);
-
- if (fSuccess)
- {
- Task.WaitAll(copyError, copyOutput);
- string output = copyOutput.Result;
- string error = copyError.Result;
-
- Console.WriteLine("createdump stdout:");
- Console.WriteLine(output);
- Console.WriteLine("createdump stderr:");
- Console.WriteLine(error);
-
- TryPrintStackTraceFromCrashReport(crashDumpPath + ".crashreport.json", outputWriter);
-
- // Ensure the dump is accessible by current user
- Process chown = new Process();
- chown.StartInfo.FileName = "sudo";
- chown.StartInfo.Arguments = $"chown \"{Environment.UserName}\" \"{crashDumpPath}\"";
-
- chown.StartInfo.UseShellExecute = false;
- chown.StartInfo.RedirectStandardOutput = true;
- chown.StartInfo.RedirectStandardError = true;
-
- Console.WriteLine($"Invoking: {chown.StartInfo.FileName} {chown.StartInfo.Arguments}");
- chown.Start();
- copyOutput = chown.StandardOutput.ReadToEndAsync();
- copyError = chown.StandardError.ReadToEndAsync();
-
- chown.WaitForExit(DEFAULT_TIMEOUT_MS);
-
- Task.WaitAll(copyError, copyOutput);
- Console.WriteLine("chown stdout:");
- Console.WriteLine(copyOutput.Result);
- Console.WriteLine("chown stderr:");
- Console.WriteLine(copyError.Result);
- }
- else
- {
- // Workaround for https://github.com/dotnet/runtime/issues/93321
- const int MaxRetries = 5;
- for (int i = 0; i < MaxRetries; i++)
- {
- try
- {
- createdump.Kill(entireProcessTree: true);
- break;
- }
- catch (Exception e) when (i < MaxRetries - 1)
- {
- Console.WriteLine($"Process.Kill(entireProcessTree: true) failed:");
- Console.WriteLine(e);
- Console.WriteLine("Retrying...");
- }
- }
- }
-
- return fSuccess && createdump.ExitCode == 0;
- }
-
- private static List knownNativeModules = new List() { "libcoreclr.so", "libclrjit.so" };
- private static string TO_BE_CONTINUE_TAG = "";
- private static string SKIP_LINE_TAG = "# ";
-
-
- static bool RunProcess(string fileName, string arguments, TextWriter outputWriter)
- {
- Process proc = new Process()
- {
- StartInfo = new ProcessStartInfo()
- {
- FileName = fileName,
- Arguments = arguments,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- }
- };
-
- outputWriter.WriteLine($"Invoking: {proc.StartInfo.FileName} {proc.StartInfo.Arguments}");
- proc.Start();
-
- Task stdOut = proc.StandardOutput.ReadToEndAsync();
- Task stdErr = proc.StandardError.ReadToEndAsync();
- if (!proc.WaitForExit(DEFAULT_TIMEOUT_MS))
- {
- proc.Kill(true);
- outputWriter.WriteLine($"Timedout: '{fileName} {arguments}");
- return false;
- }
-
- Task.WaitAll(stdOut, stdErr);
- string output = stdOut.Result;
- string error = stdErr.Result;
- if (!string.IsNullOrWhiteSpace(output))
- {
- outputWriter.WriteLine($"stdout: {output}");
- }
- if (!string.IsNullOrWhiteSpace(error))
- {
- outputWriter.WriteLine($"stderr: {error}");
- }
- return true;
- }
-
- ///
- /// Parse crashreport.json file, use llvm-symbolizer to extract symbols
- /// and recreate the stacktrace that is printed on the console.
- ///
- /// crash dump path
- /// Stream for writing logs
- /// true, if we can print the stack trace, otherwise false.
- public static bool TryPrintStackTraceFromCrashReport(string crashReportJsonFile, TextWriter outputWriter)
- {
- if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
- {
- if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
-
- Console.WriteLine("=========================================");
- string? userName = Environment.GetEnvironmentVariable("USER");
- if (string.IsNullOrEmpty(userName))
- {
- userName = "helixbot";
- }
-
- if (!RunProcess("sudo", $"chmod a+rw {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
-
- if (!RunProcess("sudo", $"chown {userName} {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
-
- Console.WriteLine("=========================================");
- if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
-
- Console.WriteLine("=========================================");
- if (!RunProcess("ls", $"-l {crashReportJsonFile}", Console.Out))
- {
- return false;
- }
- }
-
- if (!File.Exists(crashReportJsonFile))
- {
- return false;
- }
- outputWriter.WriteLine($"Printing stacktrace from '{crashReportJsonFile}'");
-
- string contents;
- try
- {
- contents = File.ReadAllText(crashReportJsonFile);
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Error reading {crashReportJsonFile}: {ex.ToString()}");
- return false;
- }
- var crashReport = JsonNode.Parse(contents)!;
- var threads = (JsonArray)crashReport["payload"]!["threads"]!;
-
- // The logic happens in 3 steps:
- // 1. Read the crashReport.json file, locate all the addresses of interest and then build
- // a string that will be passed to llvm-symbolizer. It is populated so that each address
- // is in its separate line along with the file name, etc. Some TAGS are added in the
- // string that is used in step 2.
- // 2. llvm-symbolizer is ran and above string is passed as input.
- // 3. After llvm-symbolizer completes, TAGS are used to format its output to print it in
- // the way it will be printed by sos.
-
- StringBuilder addrBuilder = new StringBuilder();
- string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT") ?? string.Empty;
- foreach (var thread in threads)
- {
-
- if (thread!["native_thread_id"] == null)
- {
- continue;
- }
-
- addrBuilder.AppendLine();
- addrBuilder.AppendLine("----------------------------------");
- addrBuilder.AppendLine($"Thread Id: {thread["native_thread_id"]}");
- addrBuilder.AppendLine(" Child SP IP Call Site");
- var stack_frames = (JsonArray)thread["stack_frames"]!;
- foreach (var frame in stack_frames)
- {
- addrBuilder.Append($"{SKIP_LINE_TAG} {frame!["stack_pointer"]} {frame["native_address"]} ");
- bool isNative = (string)frame["is_managed"]! == "false";
-
- if (isNative)
- {
- var nativeModuleName = (string)frame["native_module"]!;
- var unmanagedName = (string)frame["unmanaged_name"]!;
-
- if ((nativeModuleName != null) && (knownNativeModules.Contains(nativeModuleName)))
- {
- // Need to use llvm-symbolizer (only if module_address != 0)
- AppendAddress(addrBuilder, coreRoot, nativeModuleName, (string)frame["native_address"]!, (string)frame["module_address"]!);
- }
- else if ((nativeModuleName != null) || (unmanagedName != null))
- {
- if (nativeModuleName != null)
- {
- addrBuilder.Append($"{nativeModuleName}!");
- }
- if (unmanagedName != null)
- {
- addrBuilder.Append($"{unmanagedName}");
- }
- }
- }
- else
- {
- var fileName = (string)frame["filename"]!;
- var methodName = (string)frame["method_name"]!;
-
- if ((fileName != null) || (methodName != null))
- {
- // found the managed method name
- if (fileName != null)
- {
- addrBuilder.Append($"{fileName}!");
- }
- if (methodName != null)
- {
- addrBuilder.Append($"{methodName}");
- }
- }
- else
- {
- addrBuilder.Append($"{frame["native_address"]}");
- }
- }
- addrBuilder.AppendLine();
-
- }
- }
-
- string? symbolizerOutput = null;
-
- Process llvmSymbolizer = new Process()
- {
- StartInfo = {
- FileName = "llvm-symbolizer",
- Arguments = $"--pretty-print",
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
- }
- };
-
- outputWriter.WriteLine($"Invoking {llvmSymbolizer.StartInfo.FileName} {llvmSymbolizer.StartInfo.Arguments}");
-
- try
- {
- if (!llvmSymbolizer.Start())
- {
- outputWriter.WriteLine($"Unable to start {llvmSymbolizer.StartInfo.FileName}");
- }
-
- using (var symbolizerWriter = llvmSymbolizer.StandardInput)
- {
- symbolizerWriter.WriteLine(addrBuilder.ToString());
- }
-
- Task stdout = llvmSymbolizer.StandardOutput.ReadToEndAsync();
- Task stderr = llvmSymbolizer.StandardError.ReadToEndAsync();
- bool fSuccess = llvmSymbolizer.WaitForExit(DEFAULT_TIMEOUT_MS);
-
- Task.WaitAll(stdout, stderr);
-
- if (!fSuccess)
- {
- outputWriter.WriteLine("Errors while running llvm-symbolizer --pretty-print");
- string output = stdout.Result;
- string error = stderr.Result;
-
- Console.WriteLine("llvm-symbolizer stdout:");
- Console.WriteLine(output);
- Console.WriteLine("llvm-symbolizer stderr:");
- Console.WriteLine(error);
-
- llvmSymbolizer.Kill(true);
-
- return false;
- }
-
- symbolizerOutput = stdout.Result;
-
- }
- catch (Exception e)
- {
- outputWriter.WriteLine("Errors while running llvm-symbolizer --pretty-print");
- outputWriter.WriteLine(e.ToString());
- return false;
- }
-
- // Go through the output of llvm-symbolizer and strip all the markers we added initially.
- string[] contentsToSantize = symbolizerOutput.Split(Environment.NewLine);
- StringBuilder finalBuilder = new StringBuilder();
- for (int lineNum = 0; lineNum < contentsToSantize.Length; lineNum++)
- {
- string line = contentsToSantize[lineNum].Replace(SKIP_LINE_TAG, string.Empty);
- if (string.IsNullOrWhiteSpace(line)) continue;
-
- if (line.EndsWith(TO_BE_CONTINUE_TAG))
- {
- finalBuilder.Append(line.Replace(TO_BE_CONTINUE_TAG, string.Empty));
- continue;
- }
- finalBuilder.AppendLine(line);
- }
- outputWriter.WriteLine("Stack trace:");
- outputWriter.WriteLine(finalBuilder.ToString());
- return true;
- }
-
- private static void AppendAddress(StringBuilder sb, string coreRoot, string nativeModuleName, string native_address, string module_address)
- {
- if (module_address != "0x0")
- {
- sb.Append($"{nativeModuleName}!");
- sb.Append(TO_BE_CONTINUE_TAG);
- sb.AppendLine();
- //addrBuilder.AppendLine(frame.native_image_offset);
- ulong nativeAddress = ulong.Parse(native_address.Substring(2), System.Globalization.NumberStyles.HexNumber);
- ulong moduleAddress = ulong.Parse(module_address.Substring(2), System.Globalization.NumberStyles.HexNumber);
- string fullPathToModule = Path.Combine(coreRoot, nativeModuleName);
- sb.AppendFormat("{0} 0x{1:x}", fullPathToModule, nativeAddress - moduleAddress);
- }
- }
-
- public static bool TryPrintStackTraceFromWindowsDmp(string dmpFile, TextWriter outputWriter)
- {
- string? targetArchitecture = Environment.GetEnvironmentVariable(TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR);
- if (string.IsNullOrEmpty(targetArchitecture))
- {
- outputWriter.WriteLine($"Environment variable {TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR} is not set.");
- return false;
- }
-
- string cdbPath = $@"C:\Program Files (x86)\Windows Kits\10\Debuggers\{targetArchitecture}\cdb.exe";
- if (!File.Exists(cdbPath))
- {
- outputWriter.WriteLine($"Unable to find cdb.exe at {cdbPath}");
- return false;
- }
-
- string sosPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".dotnet", "sos", "sos.dll");
-
- string corDllArg = string.Empty;
- string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT");
- if (coreRoot is not null)
- {
- corDllArg = $".cordll -lp \"{coreRoot}\"";
- }
-
- var cdbScriptPath = Path.GetTempFileName();
- File.WriteAllText(cdbScriptPath, $$"""
- {{ corDllArg }}
- .load {{sosPath}}
- ~*k
- !clrstack -f -all
- q
- """);
-
- // cdb outputs the stacks directly, so we don't need to parse the output.
- if (!RunProcess(cdbPath, $@"-c ""$<{cdbScriptPath}"" -z ""{dmpFile}""", outputWriter))
- {
- outputWriter.WriteLine("Unable to run cdb.exe");
- return false;
- }
- return true;
- }
-
- // Finds all children processes starting with a process named childName
- // The children are sorted in the order they should be dumped
- static unsafe IEnumerable FindChildProcessesByName(Process process, string childName)
- {
- process.TryGetProcessName(out string parentProcessName);
- process.TryGetProcessId(out int parentProcessId);
- Console.WriteLine($"Finding all child processes of '{parentProcessName}' (ID: {parentProcessId}) with name '{childName}'");
-
- var children = new Stack();
- Queue childrenToCheck = new Queue();
- HashSet seen = new HashSet();
-
- seen.Add(parentProcessId);
-
- try
- {
- foreach (var child in process.GetChildren())
- childrenToCheck.Enqueue(child);
- }
- catch
- {
- // Process exited
- }
-
- while (childrenToCheck.Count != 0)
- {
- Process child = childrenToCheck.Dequeue();
-
- if (!child.TryGetProcessId(out int processId))
- continue;
-
- if (seen.Contains(processId))
- continue;
-
- if (!child.TryGetProcessName(out string processName))
- continue;
-
- Console.WriteLine($"Checking child process: '{processName}' (ID: {processId})");
- seen.Add(processId);
-
- try
- {
- foreach (var grandchild in child.GetChildren())
- childrenToCheck.Enqueue(grandchild);
- }
- catch
- {
- // Process exited
- }
-
- if (processName.Equals(childName, StringComparison.OrdinalIgnoreCase))
- {
- children.Push(child);
- }
- }
-
- return children;
- }
-
- public int RunTest(string executable, string outputFile, string errorFile, string category, string testBinaryBase, string outputDir)
- {
- Debug.Assert(outputFile != errorFile);
-
- int exitCode = -100;
-
- // If a timeout was given to us by an environment variable, use it instead of the default
- // timeout.
- string? environmentVar = Environment.GetEnvironmentVariable(TIMEOUT_ENVIRONMENT_VAR);
- int timeout = environmentVar != null ? int.Parse(environmentVar) : DEFAULT_TIMEOUT_MS;
- bool collectCrashDumps = Environment.GetEnvironmentVariable(COLLECT_DUMPS_ENVIRONMENT_VAR) != null;
- string? crashDumpFolder = Environment.GetEnvironmentVariable(CRASH_DUMP_FOLDER_ENVIRONMENT_VAR);
-
- var outputStream = new FileStream(outputFile, FileMode.Create);
- var errorStream = new FileStream(errorFile, FileMode.Create);
-
- using (var outputWriter = new StreamWriter(outputStream))
- using (var errorWriter = new StreamWriter(errorStream))
- using (Process process = new Process())
- {
- if (MobileAppHandler.IsRetryRequested(testBinaryBase))
- {
- outputWriter.WriteLine("\nWork item retry had been requested earlier - skipping test...");
- }
- else
- {
- // Windows can run the executable implicitly
- if (OperatingSystem.IsWindows())
- {
- process.StartInfo.FileName = executable;
- }
- // Non-windows needs to be told explicitly to run through /bin/bash shell
- else
- {
- process.StartInfo.FileName = "/bin/bash";
- process.StartInfo.Arguments = executable;
- }
-
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.RedirectStandardError = true;
- process.StartInfo.EnvironmentVariables.Add("__Category", category);
- process.StartInfo.EnvironmentVariables.Add("__TestBinaryBase", testBinaryBase);
- process.StartInfo.EnvironmentVariables.Add("__OutputDir", outputDir);
-
- DateTime startTime = DateTime.Now;
- process.Start();
-
- var cts = new CancellationTokenSource();
- Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream, 4096, cts.Token);
- Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream, 4096, cts.Token);
-
- if (process.WaitForExit(timeout))
- {
- // Process completed. Check process.ExitCode here.
- exitCode = process.ExitCode;
- MobileAppHandler.CheckExitCode(exitCode, testBinaryBase, category, outputWriter);
- Task.WaitAll(copyOutput, copyError);
-
- if (exitCode != 0)
- {
- // Search for dump, if created.
- if (Directory.Exists(crashDumpFolder))
- {
- outputWriter.WriteLine($"Test failed. Trying to see if dump file was created in {crashDumpFolder} since {startTime}");
- DirectoryInfo crashDumpFolderInfo = new DirectoryInfo(crashDumpFolder);
- // crashreport is only for non-windows.
- if (!OperatingSystem.IsWindows())
- {
- var dmpFilesInfo = crashDumpFolderInfo.GetFiles("*.crashreport.json").OrderByDescending(f => f.CreationTime);
- foreach (var dmpFile in dmpFilesInfo)
- {
- if (dmpFile.CreationTime < startTime)
- {
- // No new files since test started.
- outputWriter.WriteLine("Finish looking for *.crashreport.json. No new files created.");
- break;
- }
- outputWriter.WriteLine($"Processing {dmpFile.FullName}");
- TryPrintStackTraceFromCrashReport(dmpFile.FullName, outputWriter);
- }
- }
- else
- {
- var dmpFilesInfo = crashDumpFolderInfo.GetFiles("*.dmp").OrderByDescending(f => f.CreationTime);
- foreach (var dmpFile in dmpFilesInfo)
- {
- if (dmpFile.CreationTime < startTime)
- {
- // No new files since test started.
- outputWriter.WriteLine("Finished looking for *.dmp. No new files created.");
- break;
- }
- outputWriter.WriteLine($"Processing {dmpFile.FullName}");
- TryPrintStackTraceFromWindowsDmp(dmpFile.FullName, outputWriter);
- }
- }
- }
- }
- }
- else
- {
- // Timed out.
- DateTime endTime = DateTime.Now;
-
- try
- {
- cts.Cancel();
- }
- catch { }
-
- outputWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}{2}{3}, start: {4}, end: {5})",
- executable, timeout, (environmentVar != null) ? " from variable " : "", (environmentVar != null) ? TIMEOUT_ENVIRONMENT_VAR : "",
- startTime.ToString(), endTime.ToString());
- outputWriter.Flush();
- errorWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}{2}{3}, start: {4}, end: {5})",
- executable, timeout, (environmentVar != null) ? " from variable " : "", (environmentVar != null) ? TIMEOUT_ENVIRONMENT_VAR : "",
- startTime.ToString(), endTime.ToString());
- errorWriter.Flush();
-
- Console.WriteLine("Collecting diagnostic information...");
- Console.WriteLine("Snapshot of processes currently running:");
- Console.WriteLine($"\t{"ID",-6} ProcessName");
- foreach (var activeProcess in Process.GetProcesses())
- {
- activeProcess.TryGetProcessName(out string activeProcessName);
- activeProcess.TryGetProcessId(out int activeProcessId);
- Console.WriteLine($"\t{activeProcessId,-6} {activeProcessName}");
- }
-
- if (OperatingSystem.IsWindows())
- {
- Console.WriteLine("Snapshot of processes currently running (using wmic):");
- Console.WriteLine(GetAllProcessNames_wmic());
- }
-
- if (collectCrashDumps)
- {
- if (crashDumpFolder != null)
- {
- foreach (var child in FindChildProcessesByName(process, "corerun"))
- {
- string crashDumpPath = Path.Combine(Path.GetFullPath(crashDumpFolder), string.Format("crashdump_{0}.dmp", child.Id));
- Console.WriteLine($"Attempting to collect crash dump: {crashDumpPath}");
- if (CollectCrashDump(child, crashDumpPath, outputWriter))
- {
- Console.WriteLine("Collected crash dump: {0}", crashDumpPath);
- }
- else
- {
- Console.WriteLine("Failed to collect crash dump");
- }
- }
- }
- }
-
- // kill the timed out processes after we've collected dumps
- process.Kill(entireProcessTree: true);
- }
- }
-
- outputWriter.WriteLine("Test Harness Exitcode is : " + exitCode.ToString());
- outputWriter.Flush();
- errorWriter.Flush();
- }
-
- return exitCode;
- }
-
- private static string GetAllProcessNames_wmic()
- {
- // The command to execute
- string command = "wmic process get Name, ProcessId, ParentProcessId";
-
- // Start the process and capture the output
- Process process = new Process();
- process.StartInfo.FileName = "cmd.exe";
- process.StartInfo.Arguments = $"/c {command}";
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.CreateNoWindow = true;
-
- // Start the process and read the output
- process.Start();
- string output = process.StandardOutput.ReadToEnd();
- process.WaitForExit(100); // wait for 100 ms
-
- // Output the result
- return output;
- }
- }
-}
diff --git a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs b/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs
deleted file mode 100644
index 6612b27227ad96..00000000000000
--- a/src/tests/Common/Coreclr.TestWrapper/MobileAppHandler.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using System;
-using System.IO;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace CoreclrTestLib
-{
- public class MobileAppHandler
- {
- // See https://github.com/dotnet/xharness/blob/main/src/Microsoft.DotNet.XHarness.Common/CLI/ExitCode.cs
- // 78 - PACKAGE_INSTALLATION_FAILURE
- // 81 - DEVICE_NOT_FOUND
- // 82 - RETURN_CODE_NOT_SET
- // 83 - APP_LAUNCH_FAILURE
- // 84 - DEVICE_FILE_COPY_FAILURE
- // 86 - PACKAGE_INSTALLATION_TIMEOUT
- // 88 - SIMULATOR_FAILURE
- // 89 - DEVICE_FAILURE
- // 90 - APP_LAUNCH_TIMEOUT
- // 91 - ADB_FAILURE
- private static readonly int[] _knownExitCodes = new int[] { 78, 81, 82, 83, 84, 86, 88, 89, 90, 91 };
-
- public int InstallMobileApp(string platform, string category, string testBinaryBase, string reportBase, string targetOS)
- {
- return HandleMobileApp("install", platform, category, testBinaryBase, reportBase, targetOS);
- }
-
- public int UninstallMobileApp(string platform, string category, string testBinaryBase, string reportBase, string targetOS)
- {
- return HandleMobileApp("uninstall", platform, category, testBinaryBase, reportBase, targetOS);
- }
-
- private static int HandleMobileApp(string action, string platform, string category, string testBinaryBase, string reportBase, string targetOS)
- {
- int exitCode = -100;
-
- string outputFile = Path.Combine(reportBase, action, $"{category}_{action}.output.txt");
- string errorFile = Path.Combine(reportBase, action, $"{category}_{action}.error.txt");
- bool platformValueFlag = true;
- bool actionValueFlag = true;
-
- Directory.CreateDirectory(Path.Combine(reportBase, action));
- var outputStream = new FileStream(outputFile, FileMode.Create);
- var errorStream = new FileStream(errorFile, FileMode.Create);
-
- using (var outputWriter = new StreamWriter(outputStream))
- using (var errorWriter = new StreamWriter(errorStream))
- {
- if ((platform != "android") && (platform != "apple"))
- {
- outputWriter.WriteLine($"Incorrect value of platform. Provided {platform}. Valid strings are android and apple.");
- platformValueFlag = false;
- }
-
- if ((action != "install") && (action != "uninstall"))
- {
- outputWriter.WriteLine($"Incorrect value of action. Provided {action}. Valid strings are install and uninstall.");
- actionValueFlag = false;
- }
-
- if (platformValueFlag && actionValueFlag)
- {
- int timeout = 240000; // Set timeout to 4 mins, because the installation on Android arm64/32 devices could take up to 10 mins on CI
- string? dotnetCmd_raw = System.Environment.GetEnvironmentVariable("__TestDotNetCmd");
- string? xharnessCmd_raw = System.Environment.GetEnvironmentVariable("XHARNESS_CLI_PATH");
- string dotnetCmd = string.IsNullOrEmpty(dotnetCmd_raw) ? "dotnet" : dotnetCmd_raw;
- string xharnessCmd = string.IsNullOrEmpty(xharnessCmd_raw) ? "xharness" : $"exec {xharnessCmd_raw}";
- string appExtension = platform == "android" ? "apk" : "app";
-
- string cmdStr = $"{dotnetCmd} {xharnessCmd} {platform} {action}";
-
- if (platform == "android")
- {
- cmdStr += $" --package-name=net.dot.{category}";
-
- if (action == "install")
- {
- cmdStr += $" --app={testBinaryBase}/{category}.{appExtension} --output-directory={reportBase}/{action}";
- }
- }
- else // platform is apple
- {
- string targetString = "";
-
- switch (targetOS) {
- case "ios":
- targetString = "ios-device";
- break;
- case "iossimulator":
- targetString = "ios-simulator-64";
- break;
- case "tvos":
- targetString = "tvos-device";
- break;
- case "tvossimulator":
- targetString = "tvos-simulator";
- break;
- }
-
- cmdStr += $" --output-directory={reportBase}/{action} --target={targetString}";
-
- if (action == "install")
- {
- cmdStr += $" --app={testBinaryBase}/{category}.{appExtension}";
- }
- else // action is uninstall
- {
- cmdStr += $" --app=net.dot.{category}";
- }
- }
-
- if (action == "install")
- {
- cmdStr += " --timeout 00:02:30";
- }
-
- using (Process process = new Process())
- {
- if (OperatingSystem.IsWindows())
- {
- process.StartInfo.FileName = "cmd.exe";
- }
- else
- {
- process.StartInfo.FileName = "/bin/bash";
- }
-
- process.StartInfo.Arguments = ConvertCmd2Arg(cmdStr);
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardOutput = true;
- process.StartInfo.RedirectStandardError = true;
-
- DateTime startTime = DateTime.Now;
- process.Start();
-
- var cts = new CancellationTokenSource();
- Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream, 4096, cts.Token);
- Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream, 4096, cts.Token);
-
- if (process.WaitForExit(timeout))
- {
- // Process completed.
- exitCode = process.ExitCode;
- CheckExitCode(exitCode, testBinaryBase, category, outputWriter);
- Task.WaitAll(copyOutput, copyError);
- }
- else
- {
- //Time out.
- DateTime endTime = DateTime.Now;
-
- try
- {
- cts.Cancel();
- }
- catch {}
-
- outputWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})",
- cmdStr, timeout, startTime.ToString(), endTime.ToString());
- errorWriter.WriteLine("\ncmdLine:{0} Timed Out (timeout in milliseconds: {1}, start: {2}, end: {3})",
- cmdStr, timeout, startTime.ToString(), endTime.ToString());
-
- process.Kill(entireProcessTree: true);
- }
- }
- }
-
- outputWriter.WriteLine("xharness exitcode is : " + exitCode.ToString());
- outputWriter.Flush();
- errorWriter.Flush();
- }
-
- return exitCode;
- }
-
- private static string ConvertCmd2Arg(string cmd)
- {
- cmd.Replace("\"", "\"\"");
-
- string cmdPrefix;
- if(OperatingSystem.IsWindows())
- {
- cmdPrefix = "/c";
- }
- else
- {
- cmdPrefix = "-c";
- }
-
- return $"{cmdPrefix} \"{cmd}\"";
- }
-
- private static void CreateRetryFile(string fileName, int exitCode, string appName)
- {
- using (StreamWriter writer = new StreamWriter(fileName))
- {
- writer.WriteLine($"appName: {appName}; exitCode: {exitCode}");
- }
- }
-
- public static void CheckExitCode(int exitCode, string testBinaryBase, string category, StreamWriter outputWriter)
- {
- if (_knownExitCodes.Contains(exitCode))
- {
- CreateRetryFile($"{testBinaryBase}/.retry", exitCode, category);
- outputWriter.WriteLine("\nInfra issue was detected and a work item retry was requested");
- }
- }
-
- public static bool IsRetryRequested(string testBinaryBase)
- {
- return File.Exists($"{testBinaryBase}/.retry");
- }
- }
-}
diff --git a/src/tests/Common/XUnitLogChecker/XUnitLogChecker.cs b/src/tests/Common/XUnitLogChecker/XUnitLogChecker.cs
index 2c26fda5b52683..32732581a53a7d 100644
--- a/src/tests/Common/XUnitLogChecker/XUnitLogChecker.cs
+++ b/src/tests/Common/XUnitLogChecker/XUnitLogChecker.cs
@@ -3,13 +3,14 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Text;
+using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Threading;
+using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
-using CoreclrTestWrapperLib = CoreclrTestLib.CoreclrTestWrapperLib;
-
public class XUnitLogChecker
{
private static class Patterns
@@ -512,8 +513,7 @@ static void PrintStackTracesFromDumps(string testLogPath)
WriteLineTimestamp($"Reading crash dump '{dumpPath}'...");
WriteLineTimestamp("Stack Trace Found:\n");
- CoreclrTestWrapperLib.TryPrintStackTraceFromWindowsDmp(dumpPath,
- Console.Out);
+ TryPrintStackTraceFromWindowsDmp(dumpPath, Console.Out);
}
else
{
@@ -529,10 +529,336 @@ static void PrintStackTracesFromDumps(string testLogPath)
WriteLineTimestamp($"Reading crash report '{crashReportPath}'...");
WriteLineTimestamp("Stack Trace Found:\n");
- CoreclrTestWrapperLib.TryPrintStackTraceFromCrashReport(crashReportPath,
- Console.Out);
+ TryPrintStackTraceFromCrashReport(crashReportPath, Console.Out);
+ }
+ }
+ }
+
+ private const int DEFAULT_TIMEOUT_MS = 1000 * 60 * 10;
+ private const string TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR = "__TestArchitecture";
+ private static readonly List s_knownNativeModules = new List() { "libcoreclr.so", "libclrjit.so" };
+ private const string TO_BE_CONTINUE_TAG = "";
+ private const string SKIP_LINE_TAG = "# ";
+
+ static bool RunProcess(string fileName, string arguments, TextWriter outputWriter)
+ {
+ Process proc = new Process()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = fileName,
+ Arguments = arguments,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ }
+ };
+
+ outputWriter.WriteLine($"Invoking: {proc.StartInfo.FileName} {proc.StartInfo.Arguments}");
+ proc.Start();
+
+ Task stdOut = proc.StandardOutput.ReadToEndAsync();
+ Task stdErr = proc.StandardError.ReadToEndAsync();
+ if (!proc.WaitForExit(DEFAULT_TIMEOUT_MS))
+ {
+ proc.Kill(true);
+ outputWriter.WriteLine($"Timedout: '{fileName} {arguments}");
+ return false;
+ }
+
+ Task.WaitAll(stdOut, stdErr);
+ string output = stdOut.Result;
+ string error = stdErr.Result;
+ if (!string.IsNullOrWhiteSpace(output))
+ {
+ outputWriter.WriteLine($"stdout: {output}");
+ }
+ if (!string.IsNullOrWhiteSpace(error))
+ {
+ outputWriter.WriteLine($"stderr: {error}");
+ }
+ return true;
+ }
+
+ ///
+ /// Parse crashreport.json file, use llvm-symbolizer to extract symbols
+ /// and recreate the stacktrace that is printed on the console.
+ ///
+ /// crash dump path
+ /// Stream for writing logs
+ /// true, if we can print the stack trace, otherwise false.
+ static bool TryPrintStackTraceFromCrashReport(string crashReportJsonFile, TextWriter outputWriter)
+ {
+ if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
+ {
+ if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ Console.WriteLine("=========================================");
+ string? userName = Environment.GetEnvironmentVariable("USER");
+ if (string.IsNullOrEmpty(userName))
+ {
+ userName = "helixbot";
+ }
+
+ if (!RunProcess("sudo", $"chmod a+rw {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ if (!RunProcess("sudo", $"chown {userName} {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ Console.WriteLine("=========================================");
+ if (!RunProcess("sudo", $"ls -l {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+
+ Console.WriteLine("=========================================");
+ if (!RunProcess("ls", $"-l {crashReportJsonFile}", Console.Out))
+ {
+ return false;
+ }
+ }
+
+ if (!File.Exists(crashReportJsonFile))
+ {
+ return false;
+ }
+ outputWriter.WriteLine($"Printing stacktrace from '{crashReportJsonFile}'");
+
+ string contents;
+ try
+ {
+ contents = File.ReadAllText(crashReportJsonFile);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error reading {crashReportJsonFile}: {ex.ToString()}");
+ return false;
+ }
+ var crashReport = JsonNode.Parse(contents)!;
+ var threads = (JsonArray)crashReport["payload"]!["threads"]!;
+
+ // The logic happens in 3 steps:
+ // 1. Read the crashReport.json file, locate all the addresses of interest and then build
+ // a string that will be passed to llvm-symbolizer. It is populated so that each address
+ // is in its separate line along with the file name, etc. Some TAGS are added in the
+ // string that is used in step 2.
+ // 2. llvm-symbolizer is ran and above string is passed as input.
+ // 3. After llvm-symbolizer completes, TAGS are used to format its output to print it in
+ // the way it will be printed by sos.
+
+ StringBuilder addrBuilder = new StringBuilder();
+ string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT") ?? string.Empty;
+ foreach (var thread in threads)
+ {
+
+ if (thread!["native_thread_id"] is null)
+ {
+ continue;
+ }
+
+ addrBuilder.AppendLine();
+ addrBuilder.AppendLine("----------------------------------");
+ addrBuilder.AppendLine($"Thread Id: {thread["native_thread_id"]}");
+ addrBuilder.AppendLine(" Child SP IP Call Site");
+ var stack_frames = (JsonArray)thread["stack_frames"]!;
+ foreach (var frame in stack_frames)
+ {
+ addrBuilder.Append($"{SKIP_LINE_TAG} {frame!["stack_pointer"]} {frame["native_address"]} ");
+ bool isNative = (string)frame["is_managed"]! == "false";
+
+ if (isNative)
+ {
+ var nativeModuleName = (string?)frame["native_module"];
+ var unmanagedName = (string?)frame["unmanaged_name"];
+
+ if ((nativeModuleName is not null) && (s_knownNativeModules.Contains(nativeModuleName)))
+ {
+ // Need to use llvm-symbolizer (only if module_address != 0)
+ AppendAddress(addrBuilder, coreRoot, nativeModuleName, (string)frame["native_address"]!, (string)frame["module_address"]!);
+ }
+ else if ((nativeModuleName is not null) || (unmanagedName is not null))
+ {
+ if (nativeModuleName is not null)
+ {
+ addrBuilder.Append($"{nativeModuleName}!");
+ }
+ if (unmanagedName is not null)
+ {
+ addrBuilder.Append($"{unmanagedName}");
+ }
+ }
+ }
+ else
+ {
+ var fileName = (string?)frame["filename"];
+ var methodName = (string?)frame["method_name"];
+
+ if ((fileName is not null) || (methodName is not null))
+ {
+ // found the managed method name
+ if (fileName is not null)
+ {
+ addrBuilder.Append($"{fileName}!");
+ }
+ if (methodName is not null)
+ {
+ addrBuilder.Append($"{methodName}");
+ }
+ }
+ else
+ {
+ addrBuilder.Append($"{frame["native_address"]}");
+ }
+ }
+ addrBuilder.AppendLine();
+
}
}
+
+ string? symbolizerOutput = null;
+
+ Process llvmSymbolizer = new Process()
+ {
+ StartInfo = {
+ FileName = "llvm-symbolizer",
+ Arguments = $"--pretty-print",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ }
+ };
+
+ outputWriter.WriteLine($"Invoking {llvmSymbolizer.StartInfo.FileName} {llvmSymbolizer.StartInfo.Arguments}");
+
+ try
+ {
+ if (!llvmSymbolizer.Start())
+ {
+ outputWriter.WriteLine($"Unable to start {llvmSymbolizer.StartInfo.FileName}");
+ }
+
+ using (var symbolizerWriter = llvmSymbolizer.StandardInput)
+ {
+ symbolizerWriter.WriteLine(addrBuilder.ToString());
+ }
+
+ Task stdout = llvmSymbolizer.StandardOutput.ReadToEndAsync();
+ Task stderr = llvmSymbolizer.StandardError.ReadToEndAsync();
+ bool fSuccess = llvmSymbolizer.WaitForExit(DEFAULT_TIMEOUT_MS);
+
+ Task.WaitAll(stdout, stderr);
+
+ if (!fSuccess)
+ {
+ outputWriter.WriteLine("Errors while running llvm-symbolizer --pretty-print");
+ string output = stdout.Result;
+ string error = stderr.Result;
+
+ Console.WriteLine("llvm-symbolizer stdout:");
+ Console.WriteLine(output);
+ Console.WriteLine("llvm-symbolizer stderr:");
+ Console.WriteLine(error);
+
+ llvmSymbolizer.Kill(true);
+
+ return false;
+ }
+
+ symbolizerOutput = stdout.Result;
+
+ }
+ catch (Exception e)
+ {
+ outputWriter.WriteLine("Errors while running llvm-symbolizer --pretty-print");
+ outputWriter.WriteLine(e.ToString());
+ return false;
+ }
+
+ // Go through the output of llvm-symbolizer and strip all the markers we added initially.
+ string[] contentsToSantize = symbolizerOutput.Split(Environment.NewLine);
+ StringBuilder finalBuilder = new StringBuilder();
+ for (int lineNum = 0; lineNum < contentsToSantize.Length; lineNum++)
+ {
+ string line = contentsToSantize[lineNum].Replace(SKIP_LINE_TAG, string.Empty);
+ if (string.IsNullOrWhiteSpace(line)) continue;
+
+ if (line.EndsWith(TO_BE_CONTINUE_TAG))
+ {
+ finalBuilder.Append(line.Replace(TO_BE_CONTINUE_TAG, string.Empty));
+ continue;
+ }
+ finalBuilder.AppendLine(line);
+ }
+ outputWriter.WriteLine("Stack trace:");
+ outputWriter.WriteLine(finalBuilder.ToString());
+ return true;
+ }
+
+ static void AppendAddress(StringBuilder sb, string coreRoot, string nativeModuleName, string native_address, string module_address)
+ {
+ if (module_address != "0x0")
+ {
+ sb.Append($"{nativeModuleName}!");
+ sb.Append(TO_BE_CONTINUE_TAG);
+ sb.AppendLine();
+ //addrBuilder.AppendLine(frame.native_image_offset);
+ ulong nativeAddress = ulong.Parse(native_address.Substring(2), System.Globalization.NumberStyles.HexNumber);
+ ulong moduleAddress = ulong.Parse(module_address.Substring(2), System.Globalization.NumberStyles.HexNumber);
+ string fullPathToModule = Path.Combine(coreRoot, nativeModuleName);
+ sb.AppendFormat("{0} 0x{1:x}", fullPathToModule, nativeAddress - moduleAddress);
+ }
+ }
+
+ static bool TryPrintStackTraceFromWindowsDmp(string dmpFile, TextWriter outputWriter)
+ {
+ string? targetArchitecture = Environment.GetEnvironmentVariable(TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR);
+ if (string.IsNullOrEmpty(targetArchitecture))
+ {
+ outputWriter.WriteLine($"Environment variable {TEST_TARGET_ARCHITECTURE_ENVIRONMENT_VAR} is not set.");
+ return false;
+ }
+
+ string cdbPath = $@"C:\Program Files (x86)\Windows Kits\10\Debuggers\{targetArchitecture}\cdb.exe";
+ if (!File.Exists(cdbPath))
+ {
+ outputWriter.WriteLine($"Unable to find cdb.exe at {cdbPath}");
+ return false;
+ }
+
+ string sosPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".dotnet", "sos", "sos.dll");
+
+ string corDllArg = string.Empty;
+ string? coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT");
+ if (coreRoot is not null)
+ {
+ corDllArg = $".cordll -lp \"{coreRoot}\"";
+ }
+
+ var cdbScriptPath = Path.GetTempFileName();
+ File.WriteAllText(cdbScriptPath, $$"""
+ {{ corDllArg }}
+ .load {{sosPath}}
+ ~*k
+ !clrstack -f -all
+ q
+ """);
+
+ // cdb outputs the stacks directly, so we don't need to parse the output.
+ if (!RunProcess(cdbPath, $@"-c ""$<{cdbScriptPath}"" -z ""{dmpFile}""", outputWriter))
+ {
+ outputWriter.WriteLine("Unable to run cdb.exe");
+ return false;
+ }
+ return true;
}
}
diff --git a/src/tests/Common/XUnitLogChecker/XUnitLogChecker.csproj b/src/tests/Common/XUnitLogChecker/XUnitLogChecker.csproj
index 4d5a2881ac1823..5b06ca87506641 100644
--- a/src/tests/Common/XUnitLogChecker/XUnitLogChecker.csproj
+++ b/src/tests/Common/XUnitLogChecker/XUnitLogChecker.csproj
@@ -15,9 +15,4 @@
-
-
-
-
-
diff --git a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
index f6b0884719eba1..c1cb445218062f 100644
--- a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
+++ b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
@@ -8,6 +8,7 @@ public static class OptionsHelper
{
private const string InMergedTestDirectoryOption = "build_property.InMergedTestDirectory";
private const string IsMergedTestRunnerAssemblyOption = "build_property.IsMergedTestRunnerAssembly";
+ private const string BuildAsStandaloneOption = "build_property.BuildAsStandalone";
private const string CLRTestPriorityToBuildOption = "build_property.CLRTestPriorityToBuild";
private const string TestBuildModeOption = "build_property.TestBuildMode";
private const string RuntimeFlavorOption = "build_property.RuntimeFlavor";
@@ -35,6 +36,8 @@ private static bool GetBoolOption(this AnalyzerConfigOptions options, string key
internal static bool IsMergedTestRunnerAssembly(this AnalyzerConfigOptions options) => options.GetBoolOption(IsMergedTestRunnerAssemblyOption);
+ internal static bool BuildAsStandalone(this AnalyzerConfigOptions options) => options.GetBoolOption(BuildAsStandaloneOption);
+
internal static int? CLRTestPriorityToBuild(this AnalyzerConfigOptions options) => options.GetIntOption(CLRTestPriorityToBuildOption);
internal static string RuntimeFlavor(this AnalyzerConfigOptions options) => options.TryGetValue(RuntimeFlavorOption, out string? flavor) ? flavor : "CoreCLR";
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
index 424a04fb39ec63..1f0b3ab0765c91 100644
--- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
@@ -251,11 +251,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
private static void AddRunnerSource(SourceProductionContext context, ImmutableArray methods, AnalyzerConfigOptionsProvider configOptions, ImmutableDictionary aliasMap, CompData compData)
{
- bool isMergedTestRunnerAssembly = configOptions.GlobalOptions.IsMergedTestRunnerAssembly();
+ bool buildAsMergedRunner = configOptions.GlobalOptions.IsMergedTestRunnerAssembly() && !configOptions.GlobalOptions.BuildAsStandalone();
configOptions.GlobalOptions.TryGetValue("build_property.TargetOS", out string? targetOS);
string assemblyName = compData.AssemblyName;
- if (isMergedTestRunnerAssembly)
+ if (buildAsMergedRunner)
{
if (targetOS?.ToLowerInvariant() is "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst" or "android" or "browser")
{
diff --git a/src/tests/Common/helixpublishwitharcade.proj b/src/tests/Common/helixpublishwitharcade.proj
index 1e18c685532b2e..29d09f063bb463 100644
--- a/src/tests/Common/helixpublishwitharcade.proj
+++ b/src/tests/Common/helixpublishwitharcade.proj
@@ -70,8 +70,6 @@
$([MSBuild]::NormalizeDirectory($(CoreRootDirectory)))
$(TestBinDir)XUnitLogChecker\
$([MSBuild]::NormalizeDirectory($(XUnitLogCheckerDirectory)))
- $(TestBinDir)LegacyPayloads\
- $([MSBuild]::NormalizeDirectory($(LegacyPayloadsRootDirectory)))
$(TestBinDir)MergedPayloads\
$([MSBuild]::NormalizeDirectory($(MergedPayloadsRootDirectory)))
AppBundle
@@ -128,7 +126,6 @@
-
@@ -208,68 +205,6 @@
-
-
- <_XUnitWrapperDll Include="@(XUnitWrapperGrouping)" />
-
-
- <_XUnitWrapperDll Include="$(TestBinDir)**\*.XUnitWrapper.dll" Exclude="$(LegacyPayloadsRootDirectory)**\*.XUnitWrapper.dll;@(XUnitWrapperGrouping->Metadata('FullPath'))">
-
-
-
-
-
- <_XUnitWrapperDll Update="@(_XUnitWrapperDll)">
- $([MSBuild]::ValueOrDefault(%(FileName),'').Replace('.XUnitWrapper',''))
-
-
-
-
-
-
- <_FileDirectory>%(_XUnitWrapperDll.RootDir)%(Directory)
- <_PayloadGroup>%(_XUnitWrapperDll.PayloadGroup)
- <_XUnitWrapperDll>%(_XUnitWrapperDll.FullPath)
-
-
-
- <_LegacyPayloadFiles Include="$(_FileDirectory)**" />
- <_LegacyPayloadFiles Update="@(_LegacyPayloadFiles)">
-
- $(_PayloadGroup)\$([System.IO.Path]::GetRelativePath($(TestBinDir), %(FullPath)))
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @(_MergedWrapperRunScript->'%(RootDir)%(Directory)publish/*')
- $(PublishedMergedPayloadsToExclude);@(_MergedWrapperRunScript->'%(RootDir)%(Directory)$(MobileAppBundleDirName)/**')
-
-
-
-
-
-
-
-
-
@@ -298,10 +233,7 @@
<_PayloadGroups Include="$(PayloadGroups)" />
- <_ProjectsToBuild Include="testenvironment.proj" Condition="!$(IsMergedTestWrapper)">
- Scenario=$(Scenario);TestEnvFileName=$(LegacyPayloadsRootDirectory)%(_PayloadGroups.Identity)\$(TestEnvFileName);TargetsWindows=$(TestWrapperTargetsWindows);RuntimeVariant=$(_RuntimeVariant)
-
- <_ProjectsToBuild Include="testenvironment.proj" Condition="$(IsMergedTestWrapper)">
+ <_ProjectsToBuild Include="testenvironment.proj">
Scenario=$(Scenario);TestEnvFileName=$(MergedPayloadsRootDirectory)%(_PayloadGroups.Identity)\$(TestEnvFileName);TargetsWindows=$(TestWrapperTargetsWindows);RuntimeVariant=$(_RuntimeVariant)
@@ -309,25 +241,17 @@
-
-
-
-
- <_LegacyPayloadGroups Include="@(_XUnitWrapperDll->Metadata('PayloadGroup')->DistinctWithCase())" />
- <_Scenario Include="$(_Scenarios.Split(','))" />
- <_ProjectsToBuild Include="$(MSBuildProjectFile)">
- Scenario=%(_Scenario.Identity);PayloadGroups=@(_LegacyPayloadGroups);IsMergedTestWrapper=false
-
-
-
-
-
-
-
+
<_Scenario Include="$(_Scenarios.Split(','))" />
<_ProjectsToBuild Include="$(MSBuildProjectFile)">
- Scenario=%(_Scenario.Identity);PayloadGroups=@(_MergedPayloadGroups);IsMergedTestWrapper=true
+ Scenario=%(_Scenario.Identity);PayloadGroups=@(_MergedPayloadGroups)
@@ -623,29 +547,6 @@
Condition="'@(_TestExclusionListPlaceholder)' != ''" />
-
-
-
-
-
-
- $([MSBuild]::MakeRelative($(LegacyPayloadsRootDirectory), %(FullPath)))
- %(FullPath)
-
-
-
-
-
-
@@ -827,20 +728,6 @@
sos
https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/flat2/dotnet-sos/$(DotnetSosVersion)/dotnet-sos.$(DotnetSosVersion).nupkg
-
-
-
- $([MSBuild]::MakeRelative($(LegacyPayloadsRootDirectory), %(FullPath)))
- %(FullPath)
- $([System.String]::Join(' ', $([System.IO.Directory]::GetFiles(%(FullPath), '*.XUnitWrapper.dll', SearchOption.AllDirectories))).Replace($([MSBuild]::EnsureTrailingSlash(%(FullPath))),''))
-
-
-
- $(LegacyPayloadsRootDirectory)\%(PayloadGroup).zip
-
@@ -1004,18 +891,6 @@
-
- %(PayloadDirectory)
- $(_WorkaroundForNuGetMigrationsForPrepending) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs)
- $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes)))
- coreclr_tests.run.$(TargetOS).$(TargetArchitecture).$(Configuration).mch;coreclr_tests.run.$(TargetOS).$(TargetArchitecture).$(Configuration).log
-
-
-
- $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes)))
- dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs)
-
-
--arg=env:TestExclusionListPath=TestExclusionList.txt
net.dot.%(ApkFileName)
@@ -1023,12 +898,6 @@
$([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes)))
-
- $(AppleTestTarget)
- $([System.TimeSpan]::FromMinutes($(TimeoutPerTestCollectionInMinutes)))
- $(SigningCommand) dotnet $(XUnitRunnerDll) %(XUnitWrapperDlls) $(XUnitRunnerArgs)
-
-
--set-env=TestExclusionListPath=TestExclusionList.txt
$(AppleTestTarget)
diff --git a/src/tests/Common/tests.targets b/src/tests/Common/tests.targets
index 2aa062c2f8b6f9..1d81103b1ec578 100644
--- a/src/tests/Common/tests.targets
+++ b/src/tests/Common/tests.targets
@@ -11,15 +11,15 @@
<__TestRunXmlLog Condition="'$(__TestRunXmlLog)' == ''">$(__LogsDir)\TestRun.xml
-
+
+
-
-
+ <_StandaloneRunnerMarker Include="$(BaseOutputPathWithConfig)\**\*.StandaloneTestRunner" />
+
+
+ $([System.IO.Path]::ChangeExtension('%(MergedTestWrapperScripts.Identity)', '.log'))
+
-
-
-
-
@@ -38,44 +38,11 @@
-
-
-
-
- category=outerloop;category=failing
-
- collections
-
-
-
-
-
-
-
-
- $(CORE_ROOT)\xunit\xunit.console.dll
-
- -parallel none
- -parallel $(ParallelRun)
- $(XunitArgs) -html $(__TestRunHtmlLog)
- $(XunitArgs) -xml $(__TestRunXmlLog)
- $(XunitArgs) @(IncludeTraitsItems->'-trait %(Identity)', ' ')
- $(XunitArgs) @(ExcludeTraitsItems->'-notrait %(Identity)', ' ')
-
-
- $(XunitArgs) -nocolor
-
-
-
-
- <_TestAssembliesRelative Include="@(TestAssemblies -> Replace('$(BaseOutputPathWithConfig)', '.\'))" />
-
-
-
+ DependsOnTargets="FindBuildAllTestsAsStandaloneRunnerScripts;FindMergedTestDirectories">
+
+
@@ -97,20 +64,43 @@
-
-
-
- $(DotNetCli) --roll-forward latestmajor $(XunitConsoleRunner) @(TestAssemblies->'%(Identity)', ' ') $(XunitArgs)
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ Pass
+ Fail
+
+
+
+
+
+
+ <_StandaloneProjectsToBuild Include="$(MSBuildThisFileFullPath)">
+ StandaloneRunnerScript=%(StandaloneRunnerScripts.Identity)
+
+
+
+
+
+
+
+
-
+
diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props
index 1822cbe9f31195..6300b45186723f 100644
--- a/src/tests/Directory.Build.props
+++ b/src/tests/Directory.Build.props
@@ -88,12 +88,6 @@
false
- false
- true
-
- false
- true
-
false
true
llvmaot
diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets
index 74c89c69514ca5..4908a1c2198e87 100644
--- a/src/tests/Directory.Build.targets
+++ b/src/tests/Directory.Build.targets
@@ -310,7 +310,7 @@
-
+
@@ -421,6 +421,10 @@
+
+
+ StandaloneTestRunner
+
MergedTestAssembly
diff --git a/src/tests/build.cmd b/src/tests/build.cmd
index 73691be70ac458..b907eadb38f720 100644
--- a/src/tests/build.cmd
+++ b/src/tests/build.cmd
@@ -44,8 +44,6 @@ set __BuildLogRootName=TestBuild
set __SkipRestorePackages=0
set __SkipManaged=
-set __SkipTestWrappers=
-set __BuildTestWrappersOnly=
set __SkipNative=
set __CompositeBuildMode=
set __TestBuildMode=
@@ -104,12 +102,10 @@ if /i "%arg%" == "Rebuild" (set __RebuildTests=1&set processedArgs
if /i "%arg%" == "SkipRestorePackages" (set __SkipRestorePackages=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "SkipManaged" (set __SkipManaged=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "SkipNative" (set __SkipNative=1&set __CopyNativeProjectsAfterCombinedTestBuild=false&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
-if /i "%arg%" == "SkipTestWrappers" (set __SkipTestWrappers=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "SkipGenerateLayout" (set __SkipGenerateLayout=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
-if /i "%arg%" == "CopyNativeOnly" (set __CopyNativeTestBinaries=1&set __SkipNative=1&set __CopyNativeProjectsAfterCombinedTestBuild=false&set __SkipGenerateLayout=1&set __SkipTestWrappers=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
+if /i "%arg%" == "CopyNativeOnly" (set __CopyNativeTestBinaries=1&set __SkipNative=1&set __CopyNativeProjectsAfterCombinedTestBuild=false&set __SkipGenerateLayout=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "GenerateLayoutOnly" (set __GenerateLayoutOnly=1&set __SkipManaged=1&set __SkipNative=1&set __CopyNativeProjectsAfterCombinedTestBuild=false&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
-if /i "%arg%" == "BuildTestWrappersOnly" (set __SkipNative=1&set __SkipManaged=1&set __BuildTestWrappersOnly=1&set __SkipGenerateLayout=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "MSBuild" (set __Ninja=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "crossgen2" (set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%arg%" == "composite" (set __CompositeBuildMode=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
@@ -169,8 +165,6 @@ if defined __TestArgParsing (
echo.__BuildLogRootName=%__BuildLogRootName%
echo.__SkipRestorePackages=%__SkipRestorePackages%
echo.__SkipManaged=%__SkipManaged%
- echo.__SkipTestWrappers=%__SkipTestWrappers%
- echo.__BuildTestWrappersOnly=%__BuildTestWrappersOnly%
echo.__SkipNative=%__SkipNative%
echo.__CompositeBuildMode=%__CompositeBuildMode%
echo.__TestBuildMode=%__TestBuildMode%
@@ -260,7 +254,6 @@ REM ===
REM =========================================================================================
if "%__SkipNative%" == "1" goto skipnative
-if "%__BuildTestWrappersOnly%" == "1" goto skipnative
if "%__GenerateLayoutOnly%" == "1" goto skipnative
if "%__CopyNativeTestBinaries%" == "1" goto skipnative
@@ -386,12 +379,10 @@ echo -Rebuild: Clean up all test artifacts prior to building tests.
echo -SkipRestorePackages: Skip package restore.
echo -SkipManaged: Skip the managed tests build.
echo -SkipNative: Skip the native tests build.
-echo -SkipTestWrappers: Skip generating test wrappers.
echo -SkipGenerateLayout: Skip generating the Core_Root layout.
echo.
echo -CopyNativeOnly: Only copy the native test binaries to the managed output. Do not build the native or managed tests.
echo -GenerateLayoutOnly: Only generate the Core_Root layout without building managed or native test components.
-echo -BuildTestWrappersOnly: Only generate test wrappers without building managed or native test components or generating layouts.
echo -MSBuild: Use MSBuild instead of Ninja.
echo -Crossgen2: Precompiles the framework managed assemblies in coreroot using the Crossgen2 compiler.
echo -Composite: Use Crossgen2 composite mode (all framework gets compiled into a single native R2R library).
diff --git a/src/tests/build.proj b/src/tests/build.proj
index d212180561060d..baeac24e0263a7 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -2,7 +2,6 @@
-
@@ -47,73 +46,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @(LegacyRunnableTestPaths->Count())
-
-
-
-
-
-
-
-
-
-
-
-
False
True
-
+
-
@@ -202,178 +149,6 @@
/>
-
-
-
-
-
- android-$(TargetArchitecture)
- $([System.IO.Path]::GetDirectoryName($([System.IO.Path]::GetDirectoryName($(_CMDDIR)))))
- $([System.String]::Copy('$(_CMDDIR)').Replace("$(CMDDIR_Grandparent)/",""))
- $([System.String]::Copy('$(CategoryWithSlash)').Replace('/','_'))
- $(IntermediateOutputPath)\AndroidApps\$(Category)
- $(BuildDir)\apk
- $(XUnitTestBinBase)$(CategoryWithSlash)\$(Category).apk
- 127.0.0.1:9000,nosuspend,listen
- true
- $(ArtifactsBinDir)microsoft.netcore.app.runtime.android-$(TargetArchitecture)\$(Configuration)\runtimes\android-$(TargetArchitecture)\
- arm64-v8a
- armeabi-v7a
- x86_64
- x86
- false
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_RuntimeFlavor>Mono
- <_RuntimeFlavor Condition="'$(UseMonoRuntime)' == 'false' and '$(TestBuildMode)' != 'nativeaot'">CoreCLR
- <_RuntimeFlavor Condition="'$(TestBuildMode)' == 'nativeaot'">nativeaot
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $([System.IO.Path]::GetDirectoryName($([System.IO.Path]::GetDirectoryName($(_CMDDIR)))))
- $([System.IO.Path]::GetFileName($(CategoryPath)))
- $(CategoryName)/$([System.String]::Copy('$(_CMDDIR)').Replace("$(CategoryPath)/",""))
- $([System.IO.Path]::GetFileName($(_CMDDIR)))
- $([System.String]::Copy('$(TestRelativePath)').Replace('/','_'))
- $(IntermediateOutputPath)\iOSApps\$(AppName)
- $(XUnitTestBinBase)$(TestRelativePath)\$(AppName).app
-
-
-
- -
- true
- true
- $(AppDir)
- false
- false
-
-
-
-
-
-
-
-
-
-
- <_LinkerFlagsToDrop Include="@(NativeFramework->'-framework %(Identity)')" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- _CMDDIR=%(TestDirectories.Identity)
-
-
-
-
-
-
@@ -396,8 +171,10 @@
@(MergedAssemblyMarkerPaths->'%(RootDir)%(Directory)$(MobileAppBundleDirName)/**/*.$(TestScriptExtension)')
-
+
+
@@ -445,7 +222,7 @@
+ Condition="'$(__GenerateLayoutOnly)' != '1' and '$(__SkipManaged)' != '1' and !$(MonoAot) and !$(MonoFullAot)" />
@@ -517,14 +294,10 @@
-
-
+ Condition="'$(__CopyNativeTestBinaries)' != '1' and '$(__SkipGenerateLayout)' != '1' and !$(MonoAot) and !$(MonoFullAot)">
-
-
+ Condition="'$(__CopyNativeTestBinaries)' != '1' and '$(__TestBuildMode)' == 'crossgen2' and !$(MonoAot) and !$(MonoFullAot)" >
$(__TestIntermediatesDir)\crossgen.out
@@ -582,20 +350,10 @@
-
-
-
-
+ Condition="'$(__GenerateLayoutOnly)' != '1' and '$(__CopyNativeTestBinaries)' != '1' and ($(MonoAot) or $(MonoFullAot))" />
diff --git a/src/tests/build.sh b/src/tests/build.sh
index 1a556196b24902..e7f06db58d4fbf 100755
--- a/src/tests/build.sh
+++ b/src/tests/build.sh
@@ -59,7 +59,7 @@ build_Tests()
MSBUILDDEBUGPATH="${__MsbuildDebugLogsDir}"
export MSBUILDDEBUGPATH
- if [[ "$__SkipNative" != 1 && "$__BuildTestWrappersOnly" != 1 && "$__GenerateLayoutOnly" != 1 && "$__CopyNativeTestBinaries" != 1 && \
+ if [[ "$__SkipNative" != 1 && "$__GenerateLayoutOnly" != 1 && "$__CopyNativeTestBinaries" != 1 && \
"$__TargetOS" != "android" && "$__TargetOS" != "ios" && "$__TargetOS" != "iossimulator" && "$__TargetOS" != "tvos" && "$__TargetOS" != "tvossimulator" ]]; then
build_native "$__TargetOS" "$__TargetArch" "$__TestDir" "$__NativeTestIntermediatesDir" "install" "$__CMakeArgs" "CoreCLR test component"
@@ -91,7 +91,6 @@ build_Tests()
export __SkipManaged
export __SkipRestorePackages
export __SkipGenerateLayout
- export __SkipTestWrappers
export __BuildTestProject
export __BuildTestDir
export __BuildTestTree
@@ -101,7 +100,6 @@ build_Tests()
export __Priority
export __CreatePerfmap
export __CompositeBuildMode
- export __BuildTestWrappersOnly
export __GenerateLayoutOnly
export __TestBuildMode
export __MonoAot
@@ -144,12 +142,10 @@ usage_list+=("-rebuild - Clean up all test artifacts prior to building tests.")
usage_list+=("-skiprestorepackages - Skip package restore.")
usage_list+=("-skipmanaged - Skip the managed tests build.")
usage_list+=("-skipnative - Skip the native tests build.")
-usage_list+=("-skiptestwrappers - Skip generating test wrappers.")
usage_list+=("-skipgeneratelayout - Skip generating the Core_Root layout.")
usage_list+=("")
usage_list+=("-copynativeonly - Only copy the native test binaries to the managed output. Do not build the native or managed tests.")
usage_list+=("-generatelayoutonly - Only generate the Core_Root layout without building managed or native test components.")
-usage_list+=("-buildtestwrappersonly - Only generate test wrappers without building managed or native test components or generating layouts.")
usage_list+=("")
usage_list+=("-crossgen2 - Precompiles the framework managed assemblies in coreroot using the Crossgen2 compiler.")
usage_list+=("-composite - Use Crossgen2 composite mode (all framework gets compiled into a single native R2R library).")
@@ -181,7 +177,6 @@ handle_arguments_local() {
case "$opt" in
skipmanaged|-skipmanaged)
__SkipManaged=1
- __BuildTestWrappers=0
;;
skipnative|-skipnative)
@@ -189,20 +184,11 @@ handle_arguments_local() {
__CopyNativeProjectsAfterCombinedTestBuild=false
;;
- buildtestwrappersonly|-buildtestwrappersonly)
- __BuildTestWrappersOnly=1
- ;;
-
- skiptestwrappers|-skiptestwrappers)
- __SkipTestWrappers=1
- ;;
-
copynativeonly|-copynativeonly)
__SkipNative=1
__CopyNativeTestBinaries=1
__CopyNativeProjectsAfterCombinedTestBuild=false
__SkipGenerateLayout=1
- __SkipTestWrappers=1
;;
crossgen2|-crossgen2)
@@ -334,8 +320,6 @@ __IncludeTests=INCLUDE_TESTS
__ProjectDir="$__ProjectRoot"
export __ProjectDir
-__SkipTestWrappers=0
-__BuildTestWrappersOnly=0
__Compiler=clang
__ConfigureOnly=0
__CopyNativeProjectsAfterCombinedTestBuild=true
diff --git a/src/tests/run.py b/src/tests/run.py
index a8706ac7a9ab80..9a6a0ada957b58 100755
--- a/src/tests/run.py
+++ b/src/tests/run.py
@@ -1210,11 +1210,77 @@ def parse_test_results(args, tests, assemblies):
if item_lower.endswith(".testrun.xml"):
item_name = item[:-len(".testrun.xml")]
parse_test_results_xml_file(args, item, item_name, tests, assemblies)
+ elif item_lower == "standalonerunnertestresults.testrun.log":
+ found = True
+ parse_standalone_runner_results_file(args, item, tests, assemblies)
if not found:
- print("Unable to find testRun.xml. This normally means the tests did not run.")
+ print("Unable to find testRun.xml or StandaloneRunnerTestResults.testrun.log. This normally means the tests did not run.")
print("It could also mean there was a problem logging. Please run the tests again.")
+def parse_standalone_runner_results_file(args, item, tests, assemblies):
+ """ Parse test results from a standalone runner results log file
+
+ Args:
+ args : arguments
+ item : log filename in the logs directory
+ tests : list of individual test results (filled in by this function)
+ assemblies : dictionary of per-assembly aggregations (filled in by this function)
+ """
+
+ log_result_file = os.path.join(args.logs_dir, item)
+ print("Analyzing {}".format(log_result_file))
+
+ assembly_name = "StandaloneRunnerTests"
+ assembly_info = assemblies[assembly_name]
+ if assembly_info is None:
+ assembly_info = defaultdict(lambda: None, {
+ "name": assembly_name,
+ "display_name": assembly_name,
+ "is_merged_tests_run": False,
+ "time": 0.0,
+ "passed": 0,
+ "failed": 0,
+ "skipped": 0,
+ })
+
+ with open(log_result_file, 'r', encoding='utf-8') as f:
+ for line in f:
+ line = line.strip()
+ if not line:
+ continue
+
+ # Format is: ": " where Result is "Pass" or "Fail"
+ last_colon_idx = line.rfind(': ')
+ if last_colon_idx == -1:
+ continue
+
+ script_path = line[:last_colon_idx]
+ result = line[last_colon_idx + 2:]
+
+ # Derive a test name from the script path
+ test_name = os.path.basename(script_path)
+ test_name_without_ext = os.path.splitext(test_name)[0]
+
+ tests.append(defaultdict(lambda: None, {
+ "name": test_name_without_ext,
+ "test_path": script_path,
+ "result": result,
+ "time": 0.0,
+ "test_output": None,
+ "assembly_display_name": assembly_name,
+ "is_merged": False
+ }))
+
+ if result == "Pass":
+ assembly_info["passed"] += 1
+ elif result == "Fail":
+ assembly_info["failed"] += 1
+ else:
+ assembly_info["skipped"] += 1
+
+ assemblies[assembly_name] = assembly_info
+
def parse_test_results_xml_file(args, item, item_name, tests, assemblies):
""" Parse test results from a single xml results file
@@ -1245,19 +1311,12 @@ def parse_test_results_xml_file(args, item, item_name, tests, assemblies):
if len(item_name) > 0:
display_name = item_name
- # Is the results XML from a merged tests model run?
- assembly_is_merged_tests_run = False
- assembly_test_framework = assembly.attrib["test-framework"]
- # Non-merged tests give something like "xUnit.net 2.5.3.0"
- if assembly_test_framework == "XUnitWrapperGenerator-generated-runner":
- assembly_is_merged_tests_run = True
-
assembly_info = assemblies[assembly_name]
if assembly_info is None:
assembly_info = defaultdict(lambda: None, {
"name": assembly_name,
"display_name": display_name,
- "is_merged_tests_run" : assembly_is_merged_tests_run,
+ "is_merged_tests_run" : True,
"time": 0.0,
"passed": 0,
"failed": 0,
@@ -1293,23 +1352,15 @@ def parse_test_results_xml_file(args, item, item_name, tests, assemblies):
test_name += " (" + name + ")"
result = test.attrib["result"]
time = float(collection.attrib["time"])
- if assembly_is_merged_tests_run:
- # REVIEW: Even if the test is a .dll file or .CMD file and is found, we don't know how to
- # build a repro case with it.
- test_location_on_filesystem = None
- else:
- test_location_on_filesystem = find_test_from_name(args.host_os, args.test_location, name)
- if test_location_on_filesystem is None or not os.path.isfile(test_location_on_filesystem):
- test_location_on_filesystem = None
test_output = test.findtext("output")
tests.append(defaultdict(lambda: None, {
"name": test_name,
- "test_path": test_location_on_filesystem,
+ "test_path": None,
"result" : result,
"time": time,
"test_output": test_output,
"assembly_display_name": display_name,
- "is_merged": assembly_is_merged_tests_run
+ "is_merged": True
}))
if result == "Pass":
assembly_info["passed"] += 1
diff --git a/src/tests/xunit-wrappers.targets b/src/tests/xunit-wrappers.targets
deleted file mode 100644
index 9be469d94e447f..00000000000000
--- a/src/tests/xunit-wrappers.targets
+++ /dev/null
@@ -1,506 +0,0 @@
-
-
-
-
-
-
-
-
-
- <_XunitWrapperGen >
-
-
-
-
-
-
-
-
- $(XUnitTestBinBase)\$(CategoryWithSlash)
- $(MicrosoftNETCoreAppRefVersion)
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $([System.IO.Path]::GetRelativePath('$(XunitTestBinBase)', '$(_CMDDIR)'))
- $([System.String]::Copy('$(CategoryWithSlash)').Replace('\','.'))
- $([System.String]::Copy('$(CategoryWithSlash)').Replace('/','.'))
- $(Category).XUnitWrapper
- $(XunitWrapperGeneratedCSDirBase)$(Category)
- android
- apple
- false
- true
-
-
-
- <_XunitProlog Condition=" '$(_XunitProlog)'=='' and '$(IsMobile)'=='false' ">
-
-
- <_XunitProlog Condition=" '$(_XunitProlog)'=='' and '$(IsMobile)'=='true' ">
-
- {
- // This class has no code, and is never created. Its purpose is simply
- // to be the place to apply [CollectionDefinition] and all the
- // ICollectionFixture<> interfaces.
- }
-]]>
-
- <_XunitEpilog Condition=" '$(_XunitEpilog)'=='' ">
-
-
-
-
-
- testExecutable = testExecutable.Replace("\\", "/")%3B
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(Category)
-
-
-
- $([MSBuild]::MakeRelative($(_CMDDIR), %(FullPath)))
-
-
- $([MSBuild]::MakeRelative($(XunitTestBinBase), %(AllCMDs.FullPath)))
- _$([MSBuild]::ValueOrDefault(%(AllCMDs.RelativeToCMDDIR),"").Replace(".","_").Replace("\","_").Replace("-","_"))
- _$([MSBuild]::ValueOrDefault(%(AllCMDs.RelativeToCMDDIR),"").Replace($(TestScriptExtension),"").Replace(".","_").Replace("\","_").Replace("-","_"))
- _$([MSBuild]::ValueOrDefault(%(AllCMDs.RelativeToCMDDIR),"").Replace(".","_").Replace("/","_").Replace("-","_"))
- _$([MSBuild]::ValueOrDefault(%(AllCMDs.RelativeToCMDDIR), '').Replace($(TestScriptExtension),'').Replace('.','_').Replace('/','_').Replace('-','_'))
- %(AllCMDs.TestGroup)
-
- testOutput = new List()%3B
-
- try
- {
- testOutput.AddRange(System.IO.File.ReadAllLines(errorFile))%3B
- }
- catch (Exception ex)
- {
- testOutput.Add("Unable to read error file: " + errorFile)%3B
- testOutput.Add(ex.ToString())%3B
- }
-
- testOutput.Add(string.Empty)%3B
- testOutput.Add("Return code: " + ret)%3B
- testOutput.Add("Raw output file: " + outputFile)%3B
- testOutput.Add("Raw output:")%3B
-
- try
- {
- testOutput.AddRange(System.IO.File.ReadAllLines(outputFile))%3B
- }
- catch(Exception ex)
- {
- testOutput.Add("Unable to read output file: " + outputFile)%3B
- testOutput.Add(ex.ToString())%3B
- }
-
- testOutput.Add("To run the test:")%3B
- testOutput.Add("> set CORE_ROOT=" + _Global.coreRoot)%3B
- testOutput.Add("> " + testExecutable)%3B
-
- var unicodeControlCharsRegex = new Regex("%5C%5Cp{C}+")%3B
-
- // Remove all characters that have no visual or spatial representation.
- for (int i = 0%3B i < testOutput.Count%3B i++)
- {
- string line = testOutput[i]%3B
- line = unicodeControlCharsRegex.Replace(line, string.Empty)%3B
- testOutput[i] = line%3B
- }
-
- foreach (string line in testOutput)
- {
- output.WriteLine(line)%3B
- }
-
- Assert.True(ret == CoreclrTestWrapperLib.EXIT_SUCCESS_CODE, string.Join(Environment.NewLine, testOutput))%3B
- }
- }
- }
-
- ]]>
-
-
- testOutput = new List()%3B
-
- try
- {
- testOutput.AddRange(System.IO.File.ReadAllLines(errorFile))%3B
- }
- catch (Exception ex)
- {
- testOutput.Add("Unable to read error file: " + errorFile)%3B
- testOutput.Add(ex.ToString())%3B
- }
-
- testOutput.Add(string.Empty)%3B
- testOutput.Add("Return code: " + ret)%3B
- testOutput.Add("Raw output file: " + outputFile)%3B
- testOutput.Add("Raw output:")%3B
-
- try
- {
- testOutput.AddRange(System.IO.File.ReadAllLines(outputFile))%3B
- }
- catch(Exception ex)
- {
- testOutput.Add("Unable to read output file: " + outputFile)%3B
- testOutput.Add(ex.ToString())%3B
- }
-
- testOutput.Add("To run the test:")%3B
- testOutput.Add("> set CORE_ROOT=" + globalVar.coreRoot)%3B
- testOutput.Add("> " + testExecutable)%3B
-
- var unicodeControlCharsRegex = new Regex("%5C%5Cp{C}+")%3B
-
- // Remove all characters that have no visual or spatial representation.
- for (int i = 0%3B i < testOutput.Count%3B i++)
- {
- string line = testOutput[i]%3B
- line = unicodeControlCharsRegex.Replace(line, string.Empty)%3B
- testOutput[i] = line%3B
- }
-
- foreach (string line in testOutput)
- {
- output.WriteLine(line)%3B
- }
-
- // Add Android app running log to testOutput
- if (ret != CoreclrTestWrapperLib.EXIT_SUCCESS_CODE)
- {
- string androidLogFile = System.IO.Path.Combine(outputDir, "adb-logcat-net.dot." + globalVar.category + "-net.dot.MonoRunner.log")%3B
- if(File.Exists(androidLogFile))
- {
- testOutput.AddRange(System.IO.File.ReadAllLines(androidLogFile))%3B
- }
- }
-
- Assert.True(ret == CoreclrTestWrapperLib.EXIT_SUCCESS_CODE, string.Join(Environment.NewLine, testOutput))%3B
- }
- }
- }
- ]]>
-
-
-
-
-
-