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)
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)
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())
{
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);
-}
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>
#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>
#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);
// 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>
--- /dev/null
+// 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;
+}
#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++;
}
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++;
}
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);
}
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
{
fprintf(stderr, "%s", g_help);
exitCode = -1;
}
+#ifdef HOST_UNIX
PAL_TerminateEx(exitCode);
+#endif
return exitCode;
}
// 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>
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)
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;
STATIC_CONTRACT_DEBUG_ONLY;
ShutdownLogging();
+#ifdef HOST_WINDOWS
+ CreateCrashDumpIfEnabled();
+#endif
RaiseFailFastException(NULL, NULL, 0);
}
#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())
{
ShutdownLogging();
+#ifdef HOST_WINDOWS
+ CreateCrashDumpIfEnabled();
+#endif
RaiseFailFastException(NULL, NULL, 0);
UNREACHABLE();
{
*ppException = NULL;
}
+
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled()
+{
+}
+#endif
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:
#ifdef FEATURE_PERFTRACING
-#ifdef HOST_UNIX
-
void DumpDiagnosticProtocolHelper::HandleIpcMessage(DiagnosticsIpc::IpcMessage& message, IpcStream* pStream)
{
CONTRACTL
return;
}
+#ifdef HOST_UNIX
MAKE_UTF8PTR_FROMWIDE_NOTHROW(szDumpName, payload->dumpName);
if (szDumpName != nullptr)
{
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;
delete pStream;
}
-#endif // HOST_UNIX
-
#endif // FEATURE_PERFTRACING
{
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;
// 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
(fTreatAsNativeUnhandledException == FALSE)? TypeOfReportedError::UnhandledException: TypeOfReportedError::NativeThreadUnhandledException);
}
- TerminateProcess(GetCurrentProcess(), COR_E_STACKOVERFLOW);
+ CrashDumpAndTerminateProcess(COR_E_STACKOVERFLOW);
UNREACHABLE();
}
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();
return hr;
}
-
VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow)
{
STATIC_CONTRACT_THROWS;
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");
}
}
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.
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;
}
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()
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
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
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);
// Register handler for termination requests (e.g. SIGTERM)
PAL_SetTerminationRequestHandler(HandleTerminationRequest);
#endif // TARGET_UNIX
+#ifdef HOST_WINDOWS
+ InitializeCrashDump();
+#endif // HOST_WINDOWS
}
struct UpdateObjectRefInResumeContextCallbackState
LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
_ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
}
- TerminateProcess(GetCurrentProcess(), 1);
+ CrashDumpAndTerminateProcess(1);
UNREACHABLE();
}
#endif // USE_GC_INFO_DECODER
LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
_ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
}
- TerminateProcess(GetCurrentProcess(), 1);
+ CrashDumpAndTerminateProcess(1);
UNREACHABLE();
}
LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers); \
_ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH); \
} \
- TerminateProcess(GetCurrentProcess(), 1); \
+ CrashDumpAndTerminateProcess(1); \
UNREACHABLE(); \
}
GetClrInstanceId());
}
- TerminateProcess(GetCurrentProcess(), STATUS_STACK_BUFFER_OVERRUN);
+ CrashDumpAndTerminateProcess(STATUS_STACK_BUFFER_OVERRUN);
#endif // !TARGET_UNIX
}