Windows createdump support (#35381)
authorMike McLaughlin <mikem@microsoft.com>
Sat, 25 Apr 2020 05:29:16 +0000 (22:29 -0700)
committerGitHub <noreply@github.com>
Sat, 25 Apr 2020 05:29:16 +0000 (22:29 -0700)
Windows createdump support

Build a Windows version of createdump that uses MiniDumpWriteDump.

Launch that createdump from the runtime when the runtime abort because of an unhandled exception when the same environment variables as Linux are set.

Hook up the diagnostic server dump message on Windows.

Add CrashDumpAndTerminateProcess function that generates a dump if enabled and terminates
the process. Replaced varous TerminateProcess calls in the runtime with this new function.

Add resource/version info to createdump.exe

Added stack overflow and debug assert hooks before calls to RaiseFailFastException

21 files changed:
src/coreclr/src/debug/CMakeLists.txt
src/coreclr/src/debug/createdump/CMakeLists.txt
src/coreclr/src/debug/createdump/createdump.cpp
src/coreclr/src/debug/createdump/createdump.h
src/coreclr/src/debug/createdump/createdump.rc [moved from src/coreclr/src/dlls/dbgshim/resource.h with 62% similarity]
src/coreclr/src/debug/createdump/createdumpwindows.cpp [new file with mode: 0644]
src/coreclr/src/debug/createdump/main.cpp
src/coreclr/src/dlls/dbgshim/dbgshim.rc
src/coreclr/src/inc/clrconfigvalues.h
src/coreclr/src/utilcode/debug.cpp
src/coreclr/src/utilcode/hostimpl.cpp
src/coreclr/src/vm/diagnosticserver.cpp
src/coreclr/src/vm/dumpdiagnosticprotocolhelper.cpp
src/coreclr/src/vm/dumpdiagnosticprotocolhelper.h
src/coreclr/src/vm/eepolicy.cpp
src/coreclr/src/vm/eepolicy.h
src/coreclr/src/vm/excep.cpp
src/coreclr/src/vm/excep.h
src/coreclr/src/vm/exceptionhandling.cpp
src/coreclr/src/vm/exceptmacros.h
src/coreclr/src/vm/jithelpers.cpp

index 03fcc8c..4fd69d9 100644 (file)
@@ -3,6 +3,9 @@ add_subdirectory(ildbsymlib)
 add_subdirectory(ee)
 add_subdirectory(di)
 add_subdirectory(shim)
+if(CLR_CMAKE_HOST_WIN32)
+    add_subdirectory(createdump)
+endif(CLR_CMAKE_HOST_WIN32)
 if(FEATURE_SINGLE_FILE_DIAGNOSTICS)
     add_subdirectory(runtimeinfo)
 endif(FEATURE_SINGLE_FILE_DIAGNOSTICS)
index f642159..0915212 100644 (file)
@@ -2,53 +2,77 @@ project(createdump)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
-include(configure.cmake)
-
-# Set the RPATH of createdump so that it can find dependencies without needing to set LD_LIBRARY_PATH
-# For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling.
-if (CORECLR_SET_RPATH)
-  set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
-  if(CLR_CMAKE_HOST_OSX)
-    set(CMAKE_INSTALL_RPATH "@loader_path")
-  else()
-    set(CMAKE_INSTALL_RPATH "\$ORIGIN")
-  endif(CLR_CMAKE_HOST_OSX)
-endif (CORECLR_SET_RPATH)
+include_directories(BEFORE ${VM_DIR})
 
 remove_definitions(-DUNICODE)
 remove_definitions(-D_UNICODE)
 
-include_directories(BEFORE ${VM_DIR})
+if(CLR_CMAKE_HOST_WIN32)
+
+    set(CREATEDUMP_SOURCES
+        main.cpp
+        createdumpwindows.cpp
+        createdump.rc
+    )
+
+    _add_executable(createdump
+        ${CREATEDUMP_SOURCES}
+    )
+
+    target_link_libraries(createdump
+        kernel32.lib
+        ${STATIC_MT_CRT_LIB}
+        advapi32.lib
+        version.lib
+        dbghelp.lib
+    )
+
+else(CLR_CMAKE_HOST_WIN32)
+
+    include(configure.cmake)
+
+    # Set the RPATH of createdump so that it can find dependencies without needing to set LD_LIBRARY_PATH
+    # For more information: http://www.cmake.org/Wiki/CMake_RPATH_handling.
+    if (CORECLR_SET_RPATH)
+      set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+      if(CLR_CMAKE_HOST_OSX)
+        set(CMAKE_INSTALL_RPATH "@loader_path")
+      else()
+        set(CMAKE_INSTALL_RPATH "\$ORIGIN")
+      endif(CLR_CMAKE_HOST_OSX)
+    endif (CORECLR_SET_RPATH)
+
+    add_definitions(-DPAL_STDCPP_COMPAT)
 
-add_definitions(-DPAL_STDCPP_COMPAT)
+    set(CREATEDUMP_SOURCES
+        createdump.cpp
+        crashinfo.cpp
+        threadinfo.cpp
+        datatarget.cpp
+        dumpwriter.cpp
+    )
 
-set(CREATEDUMP_SOURCES
-    createdump.cpp
-    crashinfo.cpp
-    threadinfo.cpp
-    datatarget.cpp
-    dumpwriter.cpp
-)
+    _add_library(createdump_lib
+        ${CREATEDUMP_SOURCES}
+    )
 
-_add_library(createdump_lib
-    ${CREATEDUMP_SOURCES}
-)
+    _add_executable(createdump
+        main.cpp
+        ${PAL_REDEFINES_FILE}
+    )
 
-_add_executable(createdump
-    main.cpp
-    ${PAL_REDEFINES_FILE}
-)
+    add_dependencies(createdump pal_redefines_file)
 
-add_dependencies(createdump pal_redefines_file)
+    target_link_libraries(createdump
+        createdump_lib
+        corguids
+        dbgutil
+        # share the PAL in the dac module
+        mscordaccore
+    )
 
-target_link_libraries(createdump
-    createdump_lib
-    corguids
-    dbgutil
-    # share the PAL in the dac module
-    mscordaccore
-)
+    add_dependencies(createdump mscordaccore)
 
-add_dependencies(createdump mscordaccore)
+endif(CLR_CMAKE_HOST_WIN32)
 
 install_clr(TARGETS createdump ADDITIONAL_DESTINATION sharedFramework)
index a4a21f4..8299054 100644 (file)
@@ -7,37 +7,21 @@
 bool g_diagnostics = false;
 
 //
-// The common create dump code
+// The Linux create dump code
 //
 bool
-CreateDumpCommon(const char* dumpPathTemplate, MINIDUMP_TYPE minidumpType, CrashInfo* crashInfo)
+CreateDump(const char* dumpPath, int pid, MINIDUMP_TYPE minidumpType)
 {
+    ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(pid);
+    ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid, dataTarget, false);
     ReleaseHolder<DumpWriter> dumpWriter = new DumpWriter(*crashInfo);
     bool result = false;
 
-    ArrayHolder<char> dumpPath = new char[PATH_MAX];
-    snprintf(dumpPath, PATH_MAX, dumpPathTemplate, crashInfo->Pid());
-
-    const char* dumpType = "minidump";
-    switch (minidumpType)
+    // The initialize the data target's ReadVirtual support (opens /proc/$pid/mem)
+    if (!dataTarget->Initialize(crashInfo))
     {
-        case MiniDumpWithPrivateReadWriteMemory:
-            dumpType = "minidump with heap";
-            break;
-
-        case MiniDumpFilterTriage:
-            dumpType = "triage minidump";
-            break;
-
-        case MiniDumpWithFullMemory:
-            dumpType = "full dump";
-            break;
-
-        default:
-            break;
+        goto exit;
     }
-    printf("Writing %s to file %s\n", dumpType, (char*)dumpPath);
-
     // Suspend all the threads in the target process and build the list of threads
     if (!crashInfo->EnumerateAndSuspendThreads())
     {
@@ -61,13 +45,3 @@ exit:
     crashInfo->ResumeThreads();
     return result;
 }
-
-//
-// Entry point for SOS createdump command
-//
-bool
-CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget)
-{
-    ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid, dataTarget, true);
-    return CreateDumpCommon(dumpPathTemplate, minidumpType, crashInfo);
-}
index 7627fb6..96afcab 100644 (file)
 
 extern bool g_diagnostics;
 
+#ifdef HOST_UNIX
 #define TRACE(args...) \
         if (g_diagnostics) { \
             printf(args); \
         }
+#else
+#define TRACE(args, ...)
+#endif
 
+#ifdef HOST_UNIX
 #include "config.h"
+#endif
 
+#include <windows.h>
 #include <winternl.h>
 #include <winver.h>
-#include <windows.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
@@ -34,11 +40,12 @@ extern bool g_diagnostics;
 #include <cordebug.h>
 #include <xcordebug.h>
 #include <mscoree.h>
-#include <dumpcommon.h>
 typedef int T_CONTEXT;
 #include <dacprivate.h>
 #include <arrayholder.h>
 #include <releaseholder.h>
+#ifdef HOST_UNIX
+#include <dumpcommon.h>
 #include <unistd.h>
 #include <signal.h>
 #include <sys/types.h>
@@ -56,13 +63,24 @@ typedef int T_CONTEXT;
 #include <link.h>
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
+#else
+#include <dbghelp.h>
+#endif
 #include <map>
 #include <set>
 #include <vector>
 #include <array>
 #include <string>
+#ifdef HOST_UNIX
 #include "datatarget.h"
 #include "threadinfo.h"
 #include "memoryregion.h"
 #include "crashinfo.h"
 #include "dumpwriter.h"
+#endif
+
+#ifndef MAX_LONGPATH
+#define MAX_LONGPATH   1024
+#endif
+
+bool CreateDump(const char* dumpPathTemplate, int pid, MINIDUMP_TYPE minidumpType);
similarity index 62%
rename from src/coreclr/src/dlls/dbgshim/resource.h
rename to src/coreclr/src/debug/createdump/createdump.rc
index 45274ba..f531966 100644 (file)
@@ -1,3 +1,8 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
+
+#define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime Crash Dump Generator\0"
+
+#include <fxver.h>
+#include <fxver.rc>
diff --git a/src/coreclr/src/debug/createdump/createdumpwindows.cpp b/src/coreclr/src/debug/createdump/createdumpwindows.cpp
new file mode 100644 (file)
index 0000000..61e98ba
--- /dev/null
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "createdump.h"
+
+bool g_diagnostics = false;
+
+//
+// The Windows create dump code
+//
+bool
+CreateDump(const char* dumpPath, int pid, MINIDUMP_TYPE minidumpType)
+{
+    HANDLE hFile = INVALID_HANDLE_VALUE;
+    HANDLE hProcess = NULL;
+    bool result = false;
+
+    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+    if (hProcess == NULL)
+    {
+        fprintf(stderr, "Invalid process id '%d' error %d\n", pid, GetLastError());
+        goto exit;
+    }
+
+    hFile = CreateFileA(dumpPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+    {
+        fprintf(stderr, "Invalid dump path '%s' error %d\n", dumpPath, GetLastError());
+        goto exit;
+    }
+
+    // Retry the write dump on ERROR_PARTIAL_COPY
+    for (int i = 0; i < 5; i++)
+    {
+        if (MiniDumpWriteDump(hProcess, pid, hFile, minidumpType, NULL, NULL, NULL))
+        {
+            result = true;
+            break;
+        }
+        else
+        {
+            int err = GetLastError();
+            if (err != ERROR_PARTIAL_COPY)
+            {
+                fprintf(stderr, "Write dump FAILED %d\n", err);
+                break;
+            }
+        }
+    }
+
+exit:
+    if (hProcess != NULL)
+    {
+        CloseHandle(hProcess);
+    }
+
+    if (hFile != INVALID_HANDLE_VALUE)
+    {
+        CloseHandle(hFile);
+    }
+
+    return result;
+}
index ab853be..dab69df 100644 (file)
@@ -4,32 +4,48 @@
 
 #include "createdump.h"
 
+#ifdef HOST_WINDOWS
+#define DEFAULT_DUMP_PATH "%TEMP%\\"
+#define DEFAULT_DUMP_TEMPLATE "dump.%d.dmp"
+#else
+#define DEFAULT_DUMP_PATH "/tmp/"
+#define DEFAULT_DUMP_TEMPLATE "coredump.%d"
+#endif
+
 const char* g_help = "createdump [options] pid\n"
-"-f, --name - dump path and file name. The pid can be placed in the name with %d. The default is '/tmp/coredump.%d'\n"
+"-f, --name - dump path and file name. The pid can be placed in the name with %d. The default is '" DEFAULT_DUMP_PATH DEFAULT_DUMP_TEMPLATE "'\n"
 "-n, --normal - create minidump.\n"
 "-h, --withheap - create minidump with heap (default).\n"
 "-t, --triage - create triage minidump.\n"
 "-u, --full - create full core dump.\n"
 "-d, --diag - enable diagnostic messages.\n";
 
-bool CreateDumpCommon(const char* dumpPathTemplate, MINIDUMP_TYPE minidumpType, CrashInfo* crashInfo);
+bool CreateDump(const char* dumpPathTemplate, int pid, MINIDUMP_TYPE minidumpType);
 
 //
 // Main entry point
 //
 int __cdecl main(const int argc, const char* argv[])
 {
-    MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory;
+    MINIDUMP_TYPE minidumpType = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory |
+                                                 MiniDumpWithDataSegs |
+                                                 MiniDumpWithHandleData |
+                                                 MiniDumpWithUnloadedModules |
+                                                 MiniDumpWithFullMemoryInfo |
+                                                 MiniDumpWithThreadInfo |
+                                                 MiniDumpWithTokenInformation);
     const char* dumpPathTemplate = nullptr;
-    pid_t pid = 0;
+    int exitCode = 0;
+    int pid = 0;
 
-    int exitCode = PAL_InitializeDLL();
+#ifdef HOST_UNIX
+    exitCode = PAL_InitializeDLL();
     if (exitCode != 0)
     {
         fprintf(stderr, "PAL initialization FAILED %d\n", exitCode);
         return exitCode;
     }
-
+#endif
 
     // Parse the command line options and target pid
     argv++;
@@ -43,26 +59,40 @@ int __cdecl main(const int argc, const char* argv[])
             }
             else if ((strcmp(*argv, "-n") == 0) || (strcmp(*argv, "--normal") == 0))
             {
-                minidumpType = MiniDumpNormal;
+                minidumpType = (MINIDUMP_TYPE)(MiniDumpNormal |
+                                               MiniDumpWithThreadInfo);
             }
             else if ((strcmp(*argv, "-h") == 0) || (strcmp(*argv, "--withheap") == 0))
             {
-                minidumpType = MiniDumpWithPrivateReadWriteMemory;
+                minidumpType = (MINIDUMP_TYPE)(MiniDumpWithPrivateReadWriteMemory |
+                                               MiniDumpWithDataSegs |
+                                               MiniDumpWithHandleData |
+                                               MiniDumpWithUnloadedModules |
+                                               MiniDumpWithFullMemoryInfo |
+                                               MiniDumpWithThreadInfo |
+                                               MiniDumpWithTokenInformation);
             }
             else if ((strcmp(*argv, "-t") == 0) || (strcmp(*argv, "--triage") == 0))
             {
-                minidumpType = MiniDumpFilterTriage;
+                minidumpType = (MINIDUMP_TYPE)(MiniDumpFilterTriage |
+                                               MiniDumpWithThreadInfo);
             }
             else if ((strcmp(*argv, "-u") == 0) || (strcmp(*argv, "--full") == 0))
             {
-                minidumpType = MiniDumpWithFullMemory;
+                minidumpType = (MINIDUMP_TYPE)(MiniDumpWithFullMemory |
+                                               MiniDumpWithDataSegs |
+                                               MiniDumpWithHandleData |
+                                               MiniDumpWithUnloadedModules |
+                                               MiniDumpWithFullMemoryInfo |
+                                               MiniDumpWithThreadInfo |
+                                               MiniDumpWithTokenInformation);
             }
             else if ((strcmp(*argv, "-d") == 0) || (strcmp(*argv, "--diag") == 0))
             {
                 g_diagnostics = true;
             }
             else {
-                pid = atoll(*argv);
+                pid = atoi(*argv);
             }
             argv++;
         }
@@ -70,15 +100,17 @@ int __cdecl main(const int argc, const char* argv[])
 
     if (pid != 0)
     {
+        ArrayHolder<char> tmpPath = new char[MAX_LONGPATH];
+        ArrayHolder<char> dumpPath = new char[MAX_LONGPATH];
+
         if (dumpPathTemplate == nullptr)
         {
-            char tmpPath[MAX_LONGPATH];
             if (::GetTempPathA(MAX_LONGPATH, tmpPath) == 0)
             {
                 fprintf(stderr, "GetTempPath failed (0x%08x)", ::GetLastError());
                 return ::GetLastError();
             }
-            exitCode = strcat_s(tmpPath, MAX_LONGPATH, "coredump.%d");
+            exitCode = strcat_s(tmpPath, MAX_LONGPATH, DEFAULT_DUMP_TEMPLATE);
             if (exitCode != 0)
             {
                 fprintf(stderr, "strcat_s failed (%d)", exitCode);
@@ -86,16 +118,32 @@ int __cdecl main(const int argc, const char* argv[])
             }
             dumpPathTemplate = tmpPath;
         }
-        ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(pid);
-        ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid, dataTarget, false);
 
-        // The initialize the data target's ReadVirtual support (opens /proc/$pid/mem)
-        if (dataTarget->Initialize(crashInfo))
+        snprintf(dumpPath, MAX_LONGPATH, dumpPathTemplate, pid);
+
+        const char* dumpType = "minidump";
+        switch (minidumpType)
         {
-            if (!CreateDumpCommon(dumpPathTemplate, minidumpType, crashInfo))
-            {
-                exitCode = -1;
-            }
+            case MiniDumpWithPrivateReadWriteMemory:
+                dumpType = "minidump with heap";
+                break;
+
+            case MiniDumpFilterTriage:
+                dumpType = "triage minidump";
+                break;
+
+            case MiniDumpWithFullMemory:
+                dumpType = "full dump";
+                break;
+
+            default:
+                break;
+        }
+        printf("Writing %s to file %s\n", dumpType, (char*)dumpPath);
+
+        if (CreateDump(dumpPath, pid, minidumpType))
+        {
+            printf("Dump successfully written\n");
         }
         else
         {
@@ -108,6 +156,8 @@ int __cdecl main(const int argc, const char* argv[])
         fprintf(stderr, "%s", g_help);
         exitCode = -1;
     }
+#ifdef HOST_UNIX
     PAL_TerminateEx(exitCode);
+#endif
     return exitCode;
 }
index c1d53ac..9e41538 100644 (file)
@@ -2,8 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-#include "resource.h"
-
 #define FX_VER_FILEDESCRIPTION_STR "Microsoft .NET Runtime Multi-CLR Debugging Helper\0"
 
 #include <fxver.h>
index 561c480..66264ed 100644 (file)
@@ -650,6 +650,14 @@ CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubResetCacheIncr, W("VirtualCallStubR
 RETAIL_CONFIG_DWORD_INFO(INTERNAL_DisableWatsonForManagedExceptions, W("DisableWatsonForManagedExceptions"), 0, "Disable Watson and debugger launching for managed exceptions")
 
 ///
+/// Dump generation
+///
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgEnableMiniDump, W("DbgEnableMiniDump"), 0, "Enable unhandled exception crash dump generation")
+RETAIL_CONFIG_STRING_INFO(INTERNAL_DbgMiniDumpName, W("DbgMiniDumpName"), "Crash dump name")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgMiniDumpType, W("DbgMiniDumpType"), 0, "Crash dump type: 1 normal, 2 withheap, 3 triage, 4 full")
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_CreateDumpDiagnostics, W("CreateDumpDiagnostics"), 0, "Enable crash dump generation diagnostic logging")
+
+///
 /// Zap
 ///
 RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_ZapBBInstr, W("ZapBBInstr"), "", CLRConfig::EEConfig_default)
index 4833b9c..80ec783 100644 (file)
 
 extern "C" _CRTIMP int __cdecl _flushall(void);
 
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled();
+#endif
+
 // Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE.
 Volatile<LONG> g_DbgSuppressAllocationAsserts = 0;
 
@@ -180,6 +184,9 @@ VOID TerminateOnAssert()
     STATIC_CONTRACT_DEBUG_ONLY;
 
     ShutdownLogging();
+#ifdef HOST_WINDOWS
+    CreateCrashDumpIfEnabled();
+#endif
     RaiseFailFastException(NULL, NULL, 0);
 }
 
@@ -456,10 +463,13 @@ bool _DbgBreakCheck(
 #endif
     // For abort, just quit the app.
     case IDABORT:
+#ifdef HOST_WINDOWS
+        CreateCrashDumpIfEnabled();
+#endif
         TerminateProcess(GetCurrentProcess(), 1);
         break;
 
-    // Tell caller to break at the correct loction.
+    // Tell caller to break at the correct location.
     case IDRETRY:
         if (IsDebuggerPresent())
         {
@@ -823,6 +833,9 @@ void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, cons
 
     ShutdownLogging();
 
+#ifdef HOST_WINDOWS
+    CreateCrashDumpIfEnabled();
+#endif
     RaiseFailFastException(NULL, NULL, 0);
 
     UNREACHABLE();
index 29716ee..3e55bcc 100644 (file)
@@ -75,3 +75,9 @@ void GetLastThrownObjectExceptionFromThread(Exception** ppException)
 {
     *ppException = NULL;
 }
+
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled()
+{
+}
+#endif
index cb01fce..c489f30 100644 (file)
@@ -75,11 +75,9 @@ DWORD WINAPI DiagnosticServer::DiagnosticsServerThread(LPVOID)
                 EventPipeProtocolHelper::HandleIpcMessage(message, pStream);
                 break;
 
-#ifdef TARGET_UNIX
             case DiagnosticsIpc::DiagnosticServerCommandSet::Dump:
                 DumpDiagnosticProtocolHelper::HandleIpcMessage(message, pStream);
                 break;
-#endif
 
 #ifdef FEATURE_PROFAPI_ATTACH_DETACH
             case DiagnosticsIpc::DiagnosticServerCommandSet::Profiler:
index 2596943..920691f 100644 (file)
@@ -10,8 +10,6 @@
 
 #ifdef FEATURE_PERFTRACING
 
-#ifdef HOST_UNIX
-
 void DumpDiagnosticProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream)
 {
     CONTRACTL
@@ -91,6 +89,7 @@ void DumpDiagnosticProtocolHelper::GenerateCoreDump(DiagnosticsIpc::IpcMessage&
         return;
     }
 
+#ifdef HOST_UNIX
     MAKE_UTF8PTR_FROMWIDE_NOTHROW(szDumpName, payload->dumpName);
     if (szDumpName != nullptr)
     {
@@ -107,6 +106,14 @@ void DumpDiagnosticProtocolHelper::GenerateCoreDump(DiagnosticsIpc::IpcMessage&
         delete pStream;
         return;
     }
+#else
+    if (!GenerateCrashDump(payload->dumpName, payload->dumpType, payload->diagnostics))
+    {
+        DiagnosticsIpc::IpcMessage::SendErrorMessage(pStream, E_FAIL);
+        delete pStream;
+        return;
+    }
+#endif
 
     DiagnosticsIpc::IpcMessage successResponse;
     HRESULT success = S_OK;
@@ -115,6 +122,4 @@ void DumpDiagnosticProtocolHelper::GenerateCoreDump(DiagnosticsIpc::IpcMessage&
     delete pStream;
 }
 
-#endif // HOST_UNIX
-
 #endif // FEATURE_PERFTRACING
index af10a51..bd00554 100644 (file)
@@ -41,10 +41,8 @@ class DumpDiagnosticProtocolHelper
 {
 public:
     // IPC event handlers.
-#ifdef HOST_UNIX
     static void GenerateCoreDump(DiagnosticsIpc::IpcMessage& message, IpcStream *pStream); // `dotnet-dump collect`
     static void HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream);
-#endif
 
 private:
     const static uint32_t IpcStreamReadBufferSize = 8192;
index bec9914..6447bf8 100644 (file)
@@ -498,39 +498,17 @@ void SafeExitProcess(UINT exitCode, BOOL fAbort = FALSE, ShutdownCompleteAction
         // Watson code
         CONTRACT_VIOLATION(ThrowsViolation);
 
-#ifdef TARGET_UNIX
         if (fAbort)
         {
-            TerminateProcess(GetCurrentProcess(), exitCode);
+            CrashDumpAndTerminateProcess(exitCode);
+        }
+        else
+        {
+            ExitProcess(exitCode);
         }
-#endif
-
-        EEPolicy::ExitProcessViaShim(exitCode);
     }
 }
 
-// This is a helper to exit the process after coordinating with the shim. It is used by
-// SafeExitProcess above, as well as from CorHost2::ExitProcess when we know that we must
-// exit the process without doing further work to shutdown this runtime. This first attempts
-// to call back to the Shim to shutdown any other runtimes within the process.
-//
-// IMPORTANT NOTE: exercise extreme caution when adding new calls to this method. It is highly
-// likely that you want to call SafeExitProcess, or EEPolicy::HandleExitProcess instead of this.
-// This function only exists to factor some common code out of the methods mentioned above.
-
-//static
-void EEPolicy::ExitProcessViaShim(UINT exitCode)
-{
-    LIMITED_METHOD_CONTRACT;
-
-    // We must call back to the Shim in order to exit the process, as this may be just one
-    // runtime in a process with many. We need to give the other runtimes a chance to exit
-    // cleanly. If we can't make the call, or if the call fails for some reason, then we
-    // simply exit the process here, which is rude to the others, but the best we can do.
-
-    ExitProcess(exitCode);
-}
-
 //---------------------------------------------------------------------------------------
 // HandleExitProcessHelper is used to shutdown the runtime as specified by the given
 // action, then to exit the process. Note, however, that the process will not exit if
@@ -1235,7 +1213,7 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE
             (fTreatAsNativeUnhandledException == FALSE)? TypeOfReportedError::UnhandledException: TypeOfReportedError::NativeThreadUnhandledException);
     }
 
-    TerminateProcess(GetCurrentProcess(), COR_E_STACKOVERFLOW);
+    CrashDumpAndTerminateProcess(COR_E_STACKOVERFLOW);
     UNREACHABLE();
 }
 
index 6738151..f44438e 100644 (file)
@@ -145,13 +145,7 @@ private:
 
     static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource, LPCWSTR argExceptionString=NULL);
 
-    // IMPORTANT NOTE: only the following two functions should be calling ExitProcessViaShim.
-    // - CorHost2::ExitProcess
-    // - SafeExitProcess
-    friend class CorHost2;
-    friend void SafeExitProcess(UINT , BOOL , ShutdownCompleteAction);
-
-    static void ExitProcessViaShim(UINT exitCode);
+    friend void SafeExitProcess(UINT exitCode, BOOL fAbort, ShutdownCompleteAction sca);
 };
 
 void InitEEPolicy();
index 337657d..8918763 100644 (file)
@@ -2811,7 +2811,6 @@ HRESULT GetHRFromThrowable(OBJECTREF throwable)
     return hr;
 }
 
-
 VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow)
 {
     STATIC_CONTRACT_THROWS;
@@ -4032,9 +4031,12 @@ LONG WatsonLastChance(                  // EXCEPTION_CONTINUE_SEARCH, _CONTINUE_
                 GCX_PREEMP();
 
                 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: About to call RaiseFailFastException\n");
+#ifdef HOST_WINDOWS
+                CreateCrashDumpIfEnabled();
+#endif
                 RaiseFailFastException(pExceptionInfo == NULL ? NULL : pExceptionInfo->ExceptionRecord,
-                                        pExceptionInfo == NULL ? NULL : pExceptionInfo->ContextRecord,
-                                        0);
+                                       pExceptionInfo == NULL ? NULL : pExceptionInfo->ContextRecord,
+                                       0);
                 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: Return from RaiseFailFastException\n");
             }
         }
@@ -4216,6 +4218,161 @@ LONG WatsonLastChance(                  // EXCEPTION_CONTINUE_SEARCH, _CONTINUE_
     UNREACHABLE();
 } // LONG WatsonLastChance()
 
+//===========================================================================================
+//
+// Windows crash dump (createdump) support
+//
+
+#ifdef HOST_WINDOWS
+
+// Crash dump generating program arguments if enabled.
+LPCWSTR g_createDumpCommandLine = nullptr;
+
+static void 
+BuildCreateDumpCommandLine(
+    SString& commandLine,
+    LPCWSTR dumpName,
+    int dumpType,
+    bool diag)
+{
+    const char* DumpGeneratorName = "createdump.exe";
+
+    PathString coreclrPath;
+    if (WszGetModuleFileName(GetCLRModule(), coreclrPath))
+    {
+        SString::CIterator lastBackslash = coreclrPath.End();
+        if (coreclrPath.FindBack(lastBackslash, W('\\')))
+        {
+            commandLine.Set(coreclrPath, coreclrPath.Begin(), lastBackslash + 1);
+        }
+    }
+
+    commandLine.AppendPrintf("%s %d", DumpGeneratorName, GetCurrentProcessId());
+
+    if (dumpName != nullptr)
+    {
+        commandLine.AppendPrintf(" --name %S", dumpName);
+    }
+
+    const char* dumpTypeOption = nullptr;
+    switch (dumpType)
+    {
+        case 1:
+            dumpTypeOption = "--normal";
+            break;
+        case 2:
+            dumpTypeOption = "--withheap";
+            break;
+        case 3:
+            dumpTypeOption = "--triage";
+            break;
+        case 4:
+            dumpTypeOption = "--full";
+            break;
+    }
+
+    if (dumpTypeOption != nullptr)
+    {
+        commandLine.AppendPrintf(" %s", dumpTypeOption);
+    }
+
+    if (diag)
+    {
+        commandLine.AppendPrintf(" --diag");
+    }
+}
+
+static bool
+LaunchCreateDump(LPCWSTR lpCommandLine)
+{
+    bool fSuccess = false;
+
+    EX_TRY
+    {
+        SECURITY_ATTRIBUTES sa;
+        sa.nLength = sizeof(sa);
+        sa.lpSecurityDescriptor = NULL;
+        sa.bInheritHandle = FALSE;
+
+        STARTUPINFO StartupInfo;
+        memset(&StartupInfo, 0, sizeof(StartupInfo));
+        StartupInfo.cb = sizeof(StartupInfo);
+
+        PROCESS_INFORMATION processInformation;
+        if (WszCreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &processInformation))
+        {
+            WaitForSingleObject(processInformation.hProcess, INFINITE);
+            fSuccess = true;
+        }
+    }
+    EX_CATCH
+    {
+    }
+    EX_END_CATCH(SwallowAllExceptions);
+
+    return fSuccess;
+}
+
+void
+CreateCrashDumpIfEnabled()
+{
+    // If enabled, launch the create minidump utility and wait until it completes
+    if (g_createDumpCommandLine != nullptr)
+    {
+        LaunchCreateDump(g_createDumpCommandLine);
+    }
+}
+
+bool
+GenerateCrashDump(
+    LPCWSTR dumpName,
+    int dumpType,
+    bool diag)
+{
+    SString commandLine;
+    if (dumpType < 1 || dumpType > 4)
+    {
+        return false;
+    }
+    if (dumpName != nullptr && dumpName[0] == '\0')
+    {
+        dumpName = nullptr;
+    }
+    BuildCreateDumpCommandLine(commandLine, dumpName, dumpType, diag);
+    return LaunchCreateDump(commandLine);
+}
+
+void
+InitializeCrashDump()
+{
+    bool enabled = CLRConfig::IsConfigEnabled(CLRConfig::INTERNAL_DbgEnableMiniDump);
+    if (enabled)
+    {
+        LPCWSTR dumpName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgMiniDumpName);
+        int dumpType = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgMiniDumpType);
+        bool diag = CLRConfig::IsConfigEnabled(CLRConfig::INTERNAL_CreateDumpDiagnostics);
+
+        SString commandLine;
+        BuildCreateDumpCommandLine(commandLine, dumpName, dumpType, diag);
+        g_createDumpCommandLine = commandLine.GetCopyOfUnicodeString();
+    }
+}
+
+#endif // HOST_WINDOWS
+
+//************************************************************************************
+// Create crash dump if enabled and terminate process. Generates crash dumps for both
+// Windows and Linux if enabled. For Linux, it happens in TerminateProcess in the PAL.
+//************************************************************************************
+
+void CrashDumpAndTerminateProcess(UINT exitCode)
+{
+#ifdef HOST_WINDOWS
+    CreateCrashDumpIfEnabled();
+#endif
+    TerminateProcess(GetCurrentProcess(), exitCode);
+}
+
 //---------------------------------------------------------------------------------------
 //
 // This is just a simple helper to do some basic checking to see if an exception is intercepted.
@@ -4342,8 +4499,8 @@ LONG UserBreakpointFilter(EXCEPTION_POINTERS* pEP)
                        GetClrInstanceId());
     }
 
-    // Otherwise, we termintate the process.
-    TerminateProcess(GetCurrentProcess(), STATUS_BREAKPOINT);
+    // Otherwise, we terminate the process.
+    CrashDumpAndTerminateProcess(STATUS_BREAKPOINT);
 
     // Shouldn't get here ...
     return EXCEPTION_CONTINUE_EXECUTION;
@@ -4921,12 +5078,6 @@ lDone: ;
     }
     PAL_ENDTRY;
 
-    //if (param.fIgnore)
-    //{
-        // VC's try/catch ignores breakpoint or single step exceptions.  We can not continue running.
-    //    TerminateProcess(GetCurrentProcess(), pExceptionInfo->ExceptionRecord->ExceptionCode);
-    //}
-
     return param.retval;
 } // LONG InternalUnhandledExceptionFilter_Worker()
 
@@ -4973,6 +5124,10 @@ LONG InternalUnhandledExceptionFilter(
         return retval;
     }
 
+#ifdef HOST_WINDOWS
+    CreateCrashDumpIfEnabled();
+#endif
+
     BOOL fShouldOurUEFDisplayUI = ShouldOurUEFDisplayUI(pExceptionInfo);
 
     // If this is a managed exception thrown by this instance of the CLR, the exception is no one's
@@ -5715,15 +5870,6 @@ LONG ThreadBaseExceptionSwallowingFilter(PEXCEPTION_POINTERS pExceptionInfo, PVO
     return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/true);
 }
 
-//    This was the filter for new managed threads in v1.0 and v1.1.  Now used
-//     for delegate invoke, various things in the thread pool, and the
-//     class init handler.
-LONG ThreadBaseExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam)
-{
-    return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/false);
-}
-
-
 //    This is the filter that we install when transitioning an AppDomain at the base of a managed
 //     thread.  Nothing interesting will get swallowed after us.  So we never decide to continue
 //     the search.  Instead, we let it go unhandled and get the Watson report and debugging
index cde9316..2a34f3a 100644 (file)
@@ -217,12 +217,20 @@ enum UnhandledExceptionLocation
                             FatalExecutionEngineException
 };
 
+#ifdef HOST_WINDOWS
+void InitializeCrashDump();
+bool GenerateCrashDump(LPCWSTR dumpName, int dumpType, bool diag);
+void CreateCrashDumpIfEnabled();
+#endif
+
+// Generates crash dumps if enabled for both Windows and Linux
+void CrashDumpAndTerminateProcess(UINT exitCode);
+
 struct ThreadBaseExceptionFilterParam
 {
     UnhandledExceptionLocation location;
 };
 
-LONG ThreadBaseExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam);
 LONG ThreadBaseExceptionSwallowingFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam);
 LONG ThreadBaseExceptionAppDomainFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam);
 
index b83b000..1482107 100644 (file)
@@ -241,6 +241,9 @@ void InitializeExceptionHandling()
     // Register handler for termination requests (e.g. SIGTERM)
     PAL_SetTerminationRequestHandler(HandleTerminationRequest);
 #endif // TARGET_UNIX
+#ifdef HOST_WINDOWS
+    InitializeCrashDump();
+#endif // HOST_WINDOWS
 }
 
 struct UpdateObjectRefInResumeContextCallbackState
@@ -4676,7 +4679,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
                 LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
                 _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
             }
-            TerminateProcess(GetCurrentProcess(), 1);
+            CrashDumpAndTerminateProcess(1);
             UNREACHABLE();
         }
 #endif // USE_GC_INFO_DECODER
@@ -4719,7 +4722,7 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
                     LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
                     _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
                 }
-                TerminateProcess(GetCurrentProcess(), 1);
+                CrashDumpAndTerminateProcess(1);
                 UNREACHABLE();
             }
 
index 38c20de..55bfec8 100644 (file)
@@ -326,7 +326,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar
                 LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);  \
                 _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);                                 \
             }                                                                                       \
-            TerminateProcess(GetCurrentProcess(), 1);                                               \
+            CrashDumpAndTerminateProcess(1);                                                        \
             UNREACHABLE();                                                                          \
         }
 
index 63d61ae..87bf968 100644 (file)
@@ -4538,7 +4538,7 @@ void DoJITFailFast ()
                        GetClrInstanceId());
     }
 
-    TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN);
+    CrashDumpAndTerminateProcess(STATUS_STACK_BUFFER_OVERRUN);
 #endif // !TARGET_UNIX
 }