From 81f07d8b1d1ee20be0c5d2c62c817285d10e60bf Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Thu, 24 Mar 2022 20:21:10 -0700 Subject: [PATCH] Fix dotnet-dump collect on half exited process (#2959) Fix dotnet-dump collect on half exited process Issue: https://github.com/dotnet/diagnostics/issues/2950 Changed to pinvoke directly to OpenProcess to get the process handle for MiniDumpWriteDump. --- src/Tools/dotnet-dump/Dumper.Windows.cs | 21 +++++++++++++++++---- src/Tools/dotnet-dump/Dumper.cs | 6 ++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Tools/dotnet-dump/Dumper.Windows.cs b/src/Tools/dotnet-dump/Dumper.Windows.cs index 3f8b5d19a..12d4d5cc1 100644 --- a/src/Tools/dotnet-dump/Dumper.Windows.cs +++ b/src/Tools/dotnet-dump/Dumper.Windows.cs @@ -7,7 +7,6 @@ using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; -using System.Threading.Tasks; namespace Microsoft.Diagnostics.Tools.Dump { @@ -15,8 +14,16 @@ namespace Microsoft.Diagnostics.Tools.Dump { private static class Windows { - internal static void CollectDump(Process process, string outputFile, DumpTypeOption type) + internal static void CollectDump(int processId, string outputFile, DumpTypeOption type) { + // The managed Process (via Process.GetProcessById) type can not be used to get the handle for a process that is in the middle of exiting such + // that it has set an exit code, but not actually exited. This is because for compat reasons the Process class will throw in these circumstances. + using SafeProcessHandle processHandle = NativeMethods.OpenProcess(NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ, false, processId); + if (processHandle.IsInvalid) + { + throw new ArgumentException($"Invalid process id {processId} error: {Marshal.GetLastWin32Error()}"); + } + // Open the file for writing using (var stream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { @@ -63,7 +70,7 @@ namespace Microsoft.Diagnostics.Tools.Dump for (int i = 0; i < 5; i++) { // Dump the process! - if (NativeMethods.MiniDumpWriteDump(process.Handle, (uint)process.Id, stream.SafeFileHandle, dumpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) + if (NativeMethods.MiniDumpWriteDump(processHandle.DangerousGetHandle(), (uint)processId, stream.SafeFileHandle, dumpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) { break; } @@ -83,8 +90,14 @@ namespace Microsoft.Diagnostics.Tools.Dump { public const int ERROR_PARTIAL_COPY = unchecked((int)0x8007012b); + public const int PROCESS_VM_READ = 0x0010; + public const int PROCESS_QUERY_INFORMATION = 0x0400; + + [DllImport("kernel32.dll", SetLastError = true)] + public extern static SafeProcessHandle OpenProcess(int access, [MarshalAs(UnmanagedType.Bool)] bool inherit, int processId); + [DllImport("Dbghelp.dll", SetLastError = true)] - public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam); + public extern static bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId, SafeFileHandle hFile, MINIDUMP_TYPE DumpType, IntPtr ExceptionParam, IntPtr UserStreamParam, IntPtr CallbackParam); [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct MINIDUMP_EXCEPTION_INFORMATION diff --git a/src/Tools/dotnet-dump/Dumper.cs b/src/Tools/dotnet-dump/Dumper.cs index ce8ec9d5c..9e406ea28 100644 --- a/src/Tools/dotnet-dump/Dumper.cs +++ b/src/Tools/dotnet-dump/Dumper.cs @@ -92,10 +92,7 @@ namespace Microsoft.Diagnostics.Tools.Dump return 0; } - // Get the process - Process process = Process.GetProcessById(processId); - - Windows.CollectDump(process, output, type); + Windows.CollectDump(processId, output, type); } else { @@ -133,6 +130,7 @@ namespace Microsoft.Diagnostics.Tools.Dump } catch (Exception ex) when (ex is FileNotFoundException || + ex is ArgumentException || ex is DirectoryNotFoundException || ex is UnauthorizedAccessException || ex is PlatformNotSupportedException || -- 2.34.1