From 963508b9b9596e530c581f53789c62c456766707 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Mon, 28 Jan 2019 16:23:55 -0800 Subject: [PATCH] Add more logging to iis graceful shutdown (#7068) --- .../src/IISExpressDeployer.cs | 99 +++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/src/Servers/IIS/IntegrationTesting.IIS/src/IISExpressDeployer.cs b/src/Servers/IIS/IntegrationTesting.IIS/src/IISExpressDeployer.cs index e4c5cd46b1..ed910a4c30 100644 --- a/src/Servers/IIS/IntegrationTesting.IIS/src/IISExpressDeployer.cs +++ b/src/Servers/IIS/IntegrationTesting.IIS/src/IISExpressDeployer.cs @@ -453,49 +453,91 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS internal static extern bool EnumWindows(EnumWindowProc callback, IntPtr lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName,int nMaxCount); + [DllImport("kernel32.dll")] + internal static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); + } private void SendStopMessageToProcess(int pid) { - Logger.LogInformation($"Sending shutdown request to {pid}"); var found = false; - WindowsNativeMethods.EnumWindows((ptr, param) => { - WindowsNativeMethods.GetWindowThreadProcessId(ptr, out var windowProcessId); - if (pid == windowProcessId) + var extraLogging = false; + var retryCount = 5; + + while (!found && retryCount > 0) + { + Logger.LogInformation($"Sending shutdown request to {pid}"); + + WindowsNativeMethods.EnumWindows((ptr, param) => { + WindowsNativeMethods.GetWindowThreadProcessId(ptr, out var windowProcessId); + if (extraLogging) + { + Logger.LogDebug($"EnumWindow returned {ptr} belonging to {windowProcessId}"); + } + + if (pid == windowProcessId) + { + // 256 is the max length + var className = new StringBuilder(256); + + if (WindowsNativeMethods.GetClassName(ptr, className, className.Capacity) == 0) + { + throw new InvalidOperationException($"Unable to get window class name: {Marshal.GetLastWin32Error()}"); + } + + if (!string.Equals(className.ToString(), "IISEXPRESS", StringComparison.OrdinalIgnoreCase)) + { + Logger.LogDebug($"Skipping window {ptr} with class name {className}"); + // skip windows without IISEXPRESS class + return true; + } + + var hWnd = new HandleRef(null, ptr); + if (!WindowsNativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero)) + { + throw new InvalidOperationException($"Unable to PostMessage to process {pid}. LastError: {Marshal.GetLastWin32Error()}"); + } + + found = true; + return false; + } + + return true; + }, IntPtr.Zero); + + if (!found) { - // 256 is the max length - var className = new StringBuilder(256); - - if (WindowsNativeMethods.GetClassName(ptr, className, className.Capacity) == 0) - { - throw new InvalidOperationException($"Unable to get window class name: {Marshal.GetLastWin32Error()}"); - } - - if (!string.Equals(className.ToString(), "IISEXPRESS", StringComparison.OrdinalIgnoreCase)) - { - // skip windows without IISEXPRESS class - return true; - } - - var hWnd = new HandleRef(null, ptr); - if (!WindowsNativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero)) - { - throw new InvalidOperationException($"Unable to PostMessage to process {pid}. LastError: {Marshal.GetLastWin32Error()}"); - } - - found = true; - return false; + Thread.Sleep(100); } - return true; - }, IntPtr.Zero); + // Add extra logging if first try was unsuccessful + extraLogging = true; + retryCount--; + } if (!found) { + TriggerCrash(pid); + throw new InvalidOperationException($"Unable to find main window for process {pid}"); } } + private void TriggerCrash(int pid) + { + try + { + Logger.LogInformation($"Trying to crash the process {pid}"); + var process = Process.GetProcessById(pid); + // Calling CreateRemoteThread as 0x1 as thread function pointer should cause a crash + WindowsNativeMethods.CreateRemoteThread(process.Handle, IntPtr.Zero, 0, (IntPtr)1, IntPtr.Zero, 0, IntPtr.Zero); + } + catch (Exception e) + { + Logger.LogInformation(e, "Exception while trying to crash the process"); + } + } + private void GracefullyShutdownProcess(Process hostProcess) { if (hostProcess != null && !hostProcess.HasExited) @@ -514,6 +556,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS if (hostProcess.ExitCode != 0) { Logger.LogWarning($"IISExpress exit code is non-zero after graceful shutdown. Exit code: {hostProcess.ExitCode}"); + throw new InvalidOperationException($"IISExpress exit code is non-zero after graceful shutdown. Exit code: {hostProcess.ExitCode}."); } } else