diff options
Diffstat (limited to 'tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs')
-rw-r--r-- | tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs | 176 |
1 files changed, 167 insertions, 9 deletions
diff --git a/tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs b/tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs index ddb905087b..e431f3a333 100644 --- a/tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs +++ b/tests/src/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs @@ -7,12 +7,89 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.InteropServices; using System.Text; 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 enum Toolhelp32Flags : uint + { + TH32CS_INHERIT = 0x80000000, + TH32CS_SNAPHEAPLIST = 0x00000001, + TH32CS_SNAPMODULE = 0x00000008, + TH32CS_SNAPMODULE32 = 0x00000010, + TH32CS_SNAPPROCESS = 0x00000002, + TH32CS_SNAPTHREAD = 0x00000004 + }; + + public unsafe struct ProcessEntry32 + { + 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 Process32First(IntPtr snapshot, ref ProcessEntry32 entry); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool Process32Next(IntPtr snapshot, ref ProcessEntry32 entry); + } + public class CoreclrTestWrapperLib { public const int EXIT_SUCCESS_CODE = 0; @@ -22,6 +99,63 @@ namespace CoreclrTestLib public const int DEFAULT_TIMEOUT = 1000 * 60*10; public const string GC_STRESS_LEVEL = "__GCSTRESSLEVEL"; + public const string COLLECT_DUMPS_ENVIRONMENT_VAR = "__CollectDumps"; + public const string CRASH_DUMP_FOLDER_ENVIRONMENT_VAR = "__CrashDumpFolder"; + + static bool CollectCrashDump(Process process, string path) + { + using (var crashDump = File.OpenWrite(path)) + { + var flags = DbgHelp.MiniDumpType.MiniDumpWithFullMemory | DbgHelp.MiniDumpType.MiniDumpIgnoreInaccessibleMemory; + return DbgHelp.MiniDumpWriteDump(process.Handle, process.Id, crashDump.SafeFileHandle, flags, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + } + } + + static unsafe bool TryFindChildProcessByName(Process process, string childName, out Process child) + { + IntPtr snapshot = Kernel32.CreateToolhelp32Snapshot(Kernel32.Toolhelp32Flags.TH32CS_SNAPPROCESS, 0); + if (snapshot == IntPtr.Zero) + { + child = null; + return false; + } + + try + { + int ppid = process.Id; + + var processEntry = new Kernel32.ProcessEntry32 { Size = sizeof(Kernel32.ProcessEntry32) }; + + bool success = Kernel32.Process32First(snapshot, ref processEntry); + while (success) + { + if (processEntry.ParentProcessID == ppid) + { + try + { + Process c = Process.GetProcessById(processEntry.ProcessID); + if (c.ProcessName.Equals(childName, StringComparison.OrdinalIgnoreCase)) + { + child = c; + return true; + } + c.Dispose(); + } + catch {} + } + + success = Kernel32.Process32Next(snapshot, ref processEntry); + } + + child = null; + return false; + } + finally + { + Kernel32.CloseHandle(snapshot); + } + } + public int RunTest(string executable, string outputFile, string errorFile) { Debug.Assert(outputFile != errorFile); @@ -39,6 +173,9 @@ namespace CoreclrTestLib string operatingSystem = System.Environment.GetEnvironmentVariable("OS"); bool runningInWindows = (operatingSystem != null && operatingSystem.StartsWith("Windows")); + // We can't yet take crash dumps on non-Windows OSs for timed-out tests + bool collectCrashDumps = runningInWindows && Environment.GetEnvironmentVariable(COLLECT_DUMPS_ENVIRONMENT_VAR) != null; + var outputStream = new FileStream(outputFile, FileMode.Create); var errorStream = new FileStream(errorFile, FileMode.Create); @@ -71,23 +208,44 @@ namespace CoreclrTestLib process.Start(); - Task copyOutput = process.StandardOutput.BaseStream.CopyToAsync(outputStream); - Task copyError = process.StandardError.BaseStream.CopyToAsync(errorStream); - - bool completed = process.WaitForExit(timeout); - copyOutput.Wait(timeout); - copyError.Wait(timeout); + 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 (completed) + if (process.WaitForExit(timeout)) { // Process completed. Check process.ExitCode here. exitCode = process.ExitCode; + Task.WaitAll(copyOutput, copyError); } else { // Timed out. - outputWriter.WriteLine("cmdLine:" + executable + " Timed Out"); - errorWriter.WriteLine("cmdLine:" + executable + " Timed Out"); + try + { + cts.Cancel(); + } + catch {} + + outputWriter.WriteLine("\ncmdLine:" + executable + " Timed Out"); + errorWriter.WriteLine("\ncmdLine:" + executable + " Timed Out"); + + if (collectCrashDumps) + { + string crashDumpFolder = Environment.GetEnvironmentVariable(CRASH_DUMP_FOLDER_ENVIRONMENT_VAR); + if (crashDumpFolder != null) + { + Process childProcess; + if (TryFindChildProcessByName(process, "corerun", out childProcess)) + { + string crashDumpPath = Path.Combine(Path.GetFullPath(crashDumpFolder), string.Format("crashdump_{0}.dmp", childProcess.Id)); + if (CollectCrashDump(childProcess, crashDumpPath)) + { + Console.WriteLine("Collected crash dump {0}", crashDumpPath); + } + } + } + } } outputWriter.WriteLine("Test Harness Exitcode is : " + exitCode.ToString()); |