diff options
author | Mike McLaughlin <mikem@microsoft.com> | 2019-05-08 00:46:51 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-08 00:46:51 -0700 |
commit | 794f2f89399026246562181bc999a5027fb6583d (patch) | |
tree | d87fc6ee11a0b925a678dc5500a42cbc707ea58f /src | |
parent | 653e25ec4780cfb7132dcb55bda20dbcc9c2a027 (diff) | |
download | coreclr-794f2f89399026246562181bc999a5027fb6583d.tar.gz coreclr-794f2f89399026246562181bc999a5027fb6583d.tar.bz2 coreclr-794f2f89399026246562181bc999a5027fb6583d.zip |
Add generate crash dump command to diagnostics server (#24460)
Add the DiagnosticProtocolHelper class to deserialize and dispatch
the new GenerateCoreDump command.
Refactor the PAL createdump launch on unhandled exception code to
used by a new PAL_GenerateCoreDump method that doesn't depend on
the complus dump environment variables.
Changed the "full" createdump not to include the uncommitted pages and
removed the "add module metadata" workaround for SOS clrstack !UNKNOWN
problem now that is fixed in SOS (crashinfo.cpp).
Diffstat (limited to 'src')
-rw-r--r-- | src/debug/createdump/crashinfo.cpp | 10 | ||||
-rw-r--r-- | src/pal/inc/pal.h | 8 | ||||
-rw-r--r-- | src/pal/src/thread/process.cpp | 268 | ||||
-rw-r--r-- | src/vm/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/vm/diagnosticprotocolhelper.cpp | 83 | ||||
-rw-r--r-- | src/vm/diagnosticprotocolhelper.h | 28 | ||||
-rw-r--r-- | src/vm/diagnosticserver.cpp | 7 | ||||
-rw-r--r-- | src/vm/diagnosticserver.h | 7 |
8 files changed, 317 insertions, 95 deletions
diff --git a/src/debug/createdump/crashinfo.cpp b/src/debug/createdump/crashinfo.cpp index 5539a1f9e5..d7a5b20961 100644 --- a/src/debug/createdump/crashinfo.cpp +++ b/src/debug/createdump/crashinfo.cpp @@ -191,16 +191,16 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) } for (const MemoryRegion& region : m_otherMappings) { - InsertMemoryBackedRegion(region); + // Don't add uncommitted pages to the full dump + if ((region.Permissions() & (PF_R | PF_W | PF_X)) != 0) + { + InsertMemoryBackedRegion(region); + } } } // Add all the heap (read/write) memory regions (m_otherMappings contains the heaps) else if (minidumpType & MiniDumpWithPrivateReadWriteMemory) { - for (const MemoryRegion& region : m_moduleMappings) - { - InsertMemoryBackedRegion(region); - } for (const MemoryRegion& region : m_otherMappings) { if (region.Permissions() == (PF_R | PF_W)) diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index f4a726e89f..a2b15f3d12 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -450,6 +450,14 @@ PALAPI PAL_SetShutdownCallback( IN PSHUTDOWN_CALLBACK callback); +PALIMPORT +BOOL +PALAPI +PAL_GenerateCoreDump( + IN LPCSTR dumpName, + IN INT dumpType, + IN BOOL diag); + typedef VOID (*PPAL_STARTUP_CALLBACK)( char *modulePath, HMODULE hModule, diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 445b75cc68..8b9fec421b 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -3206,119 +3206,107 @@ PROCNotifyProcessShutdown() /*++ Function - PROCAbortInitialize() + PROCBuildCreateDumpCommandLine Abstract - Initialize the process abort crash dump program file path and - name. Doing all of this ahead of time so nothing is allocated - or copied in PROCAbort/signal handler. + Builds the createdump command line from the arguments. Return TRUE - succeeds, FALSE - fails --*/ BOOL -PROCAbortInitialize() +PROCBuildCreateDumpCommandLine(const char** argv, char* dumpName, char* dumpType, BOOL diag) { - char* enabled = getenv("COMPlus_DbgEnableMiniDump"); - if (enabled != nullptr && _stricmp(enabled, "1") == 0) + if (g_szCoreCLRPath == nullptr) { - if (g_szCoreCLRPath == nullptr) - { - return FALSE; - } - const char* DumpGeneratorName = "createdump"; - int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName) + 1; - char* program = (char*)InternalMalloc(programLen); - if (program == nullptr) - { - return FALSE; - } - if (strcpy_s(program, programLen, g_szCoreCLRPath) != SAFECRT_SUCCESS) - { - return FALSE; - } - char *last = strrchr(program, '/'); - if (last != nullptr) - { - *(last + 1) = '\0'; - } - else - { - program[0] = '\0'; - } - if (strcat_s(program, programLen, DumpGeneratorName) != SAFECRT_SUCCESS) + return FALSE; + } + const char* DumpGeneratorName = "createdump"; + int programLen = strlen(g_szCoreCLRPath) + strlen(DumpGeneratorName) + 1; + char* program = (char*)InternalMalloc(programLen); + if (program == nullptr) + { + return FALSE; + } + if (strcpy_s(program, programLen, g_szCoreCLRPath) != SAFECRT_SUCCESS) + { + return FALSE; + } + char *last = strrchr(program, '/'); + if (last != nullptr) + { + *(last + 1) = '\0'; + } + else + { + program[0] = '\0'; + } + if (strcat_s(program, programLen, DumpGeneratorName) != SAFECRT_SUCCESS) + { + return FALSE; + } + char* pidarg = (char*)InternalMalloc(128); + if (pidarg == nullptr) + { + return FALSE; + } + if (sprintf_s(pidarg, 128, "%d", gPID) == -1) + { + return FALSE; + } + *argv++ = program; + + if (dumpName != nullptr) + { + *argv++ = "--name"; + *argv++ = dumpName; + } + + if (dumpType != nullptr) + { + if (strcmp(dumpType, "1") == 0) { - return FALSE; + *argv++ = "--normal"; } - char* pidarg = (char*)InternalMalloc(128); - if (pidarg == nullptr) + else if (strcmp(dumpType, "2") == 0) { - return FALSE; + *argv++ = "--withheap"; } - if (sprintf_s(pidarg, 128, "%d", gPID) == -1) + else if (strcmp(dumpType, "3") == 0) { - return FALSE; + *argv++ = "--triage"; } - const char** argv = (const char**)g_argvCreateDump; - *argv++ = program; - - char* envvar = getenv("COMPlus_DbgMiniDumpName"); - if (envvar != nullptr) + else if (strcmp(dumpType, "4") == 0) { - *argv++ = "--name"; - *argv++ = envvar; + *argv++ = "--full"; } + } - envvar = getenv("COMPlus_DbgMiniDumpType"); - if (envvar != nullptr) - { - if (strcmp(envvar, "1") == 0) - { - *argv++ = "--normal"; - } - else if (strcmp(envvar, "2") == 0) - { - *argv++ = "--withheap"; - } - else if (strcmp(envvar, "3") == 0) - { - *argv++ = "--triage"; - } - else if (strcmp(envvar, "4") == 0) - { - *argv++ = "--full"; - } - } + if (diag) + { + *argv++ = "--diag"; + } - envvar = getenv("COMPlus_CreateDumpDiagnostics"); - if (envvar != nullptr && strcmp(envvar, "1") == 0) - { - *argv++ = "--diag"; - } + *argv++ = pidarg; + *argv = nullptr; - *argv++ = pidarg; - *argv = nullptr; - } return TRUE; } /*++ Function: - PROCCreateCrashDumpIfEnabled + PROCCreateCrashDump - Creates crash dump of the process (if enabled). Can be - called from the unhandled native exception handler. + Creates crash dump of the process. Can be called from the + unhandled native exception handler. (no return value) --*/ -VOID -PROCCreateCrashDumpIfEnabled() +BOOL +PROCCreateCrashDump(char** argv) { #if HAVE_PRCTL_H && HAVE_PR_SET_PTRACER - // If enabled, launch the create minidump utility and wait until it completes - if (g_argvCreateDump[0] == nullptr) - return; // Fork the core dump child process. pid_t childpid = fork(); @@ -3326,14 +3314,16 @@ PROCCreateCrashDumpIfEnabled() // If error, write an error to trace log and abort if (childpid == -1) { - ERROR("PROCAbort: fork() FAILED %d (%s)\n", errno, strerror(errno)); + ERROR("PROCCreateCrashDump: fork() FAILED %d (%s)\n", errno, strerror(errno)); + return false; } else if (childpid == 0) { // Child process - if (execve(g_argvCreateDump[0], g_argvCreateDump, palEnvironment) == -1) + if (execve(argv[0], argv, palEnvironment) == -1) { - ERROR("PROCAbort: execve FAILED %d (%s)\n", errno, strerror(errno)); + ERROR("PPROCCreateCrashDump: execve FAILED %d (%s)\n", errno, strerror(errno)); + return false; } } else @@ -3341,18 +3331,122 @@ PROCCreateCrashDumpIfEnabled() // Gives the child process permission to use /proc/<pid>/mem and ptrace if (prctl(PR_SET_PTRACER, childpid, 0, 0, 0) == -1) { - ERROR("PROCAbort: prctl() FAILED %d (%s)\n", errno, strerror(errno)); + ERROR("PPROCCreateCrashDump: prctl() FAILED %d (%s)\n", errno, strerror(errno)); + return false; } // Parent waits until the child process is done - int wstatus; + int wstatus = 0; int result = waitpid(childpid, &wstatus, 0); if (result != childpid) { - ERROR("PROCAbort: waitpid FAILED result %d wstatus %d errno %d (%s)\n", + ERROR("PPROCCreateCrashDump: waitpid FAILED result %d wstatus %d errno %d (%s)\n", result, wstatus, errno, strerror(errno)); + return false; } + return !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) == 0; } #endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER + return true; +} + +/*++ +Function + PROCAbortInitialize() + +Abstract + Initialize the process abort crash dump program file path and + name. Doing all of this ahead of time so nothing is allocated + or copied in PROCAbort/signal handler. + +Return + TRUE - succeeds, FALSE - fails + +--*/ +BOOL +PROCAbortInitialize() +{ + char* enabled = getenv("COMPlus_DbgEnableMiniDump"); + if (enabled != nullptr && _stricmp(enabled, "1") == 0) + { + char* dumpName = getenv("COMPlus_DbgMiniDumpName"); + char* dumpType = getenv("COMPlus_DbgMiniDumpType"); + char* diagStr = getenv("COMPlus_CreateDumpDiagnostics"); + BOOL diag = diagStr != nullptr && strcmp(diagStr, "1") == 0; + + if (!PROCBuildCreateDumpCommandLine((const char **)g_argvCreateDump, dumpName, dumpType, diag)) + { + return FALSE; + } + } + return TRUE; +} + +/*++ +Function: + PAL_GenerateCoreDump + +Abstract: + Public entry point to create a crash dump of the process. + +Parameters: + dumpName + dumpType: + Normal = 1, + WithHeap = 2, + Triage = 3, + Full = 4 + diag + true - log createdump diagnostics to console + +Return: + TRUE success + FALSE failed +--*/ +BOOL +PAL_GenerateCoreDump( + LPCSTR dumpName, + INT dumpType, + BOOL diag) +{ + char* argvCreateDump[8] = { nullptr }; + char dumpTypeStr[16]; + + if (dumpType < 1 || dumpType > 4) + { + return FALSE; + } + if (dumpName != nullptr && dumpName[0] == '\0') + { + dumpName = nullptr; + } + if (_itoa_s(dumpType, dumpTypeStr, sizeof(dumpTypeStr), 10) != 0) + { + return FALSE; + } + if (!PROCBuildCreateDumpCommandLine((const char **)argvCreateDump, (char*)dumpName, dumpTypeStr, diag)) + { + return FALSE; + } + return PROCCreateCrashDump(argvCreateDump); +} + +/*++ +Function: + PROCCreateCrashDumpIfEnabled + + Creates crash dump of the process (if enabled). Can be + called from the unhandled native exception handler. + +(no return value) +--*/ +VOID +PROCCreateCrashDumpIfEnabled() +{ + // If enabled, launch the create minidump utility and wait until it completes + if (g_argvCreateDump[0] != nullptr) + { + PROCCreateCrashDump(g_argvCreateDump); + } } /*++ diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 8fb311ed2d..76adac0362 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -58,6 +58,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON debughelp.cpp debuginfostore.cpp decodemd.cpp + diagnosticprotocolhelper.cpp disassembler.cpp dllimport.cpp domainfile.cpp diff --git a/src/vm/diagnosticprotocolhelper.cpp b/src/vm/diagnosticprotocolhelper.cpp new file mode 100644 index 0000000000..c7b07c87f8 --- /dev/null +++ b/src/vm/diagnosticprotocolhelper.cpp @@ -0,0 +1,83 @@ +// 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 "common.h" +#include "fastserializer.h" +#include "diagnosticprotocolhelper.h" +#include "diagnosticsipc.h" +#include "diagnosticsprotocol.h" + +#ifdef FEATURE_PERFTRACING +#ifdef FEATURE_PAL + +static void WriteStatus(uint64_t result, IpcStream* pStream) +{ + uint32_t nBytesWritten = 0; + bool fSuccess = pStream->Write(&result, sizeof(result), nBytesWritten); + if (fSuccess) + { + fSuccess = pStream->Flush(); + } +} + +void DiagnosticProtocolHelper::GenerateCoreDump(IpcStream* pStream) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + PRECONDITION(pStream != nullptr); + } + CONTRACTL_END; + + if (pStream == nullptr) + return; + + HRESULT hr = S_OK; + + // TODO: Read within a loop. + uint8_t buffer[IpcStreamReadBufferSize] { }; + uint32_t nNumberOfBytesRead = 0; + bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead); + if (fSuccess) + { + // The protocol buffer is defined as: + // string - dumpName (array<char> where the last char must = 0) or (length = 0) + // int - dumpType + // int - diagnostics + // returns + // ulong - status + LPCSTR dumpName; + INT dumpType; + INT diagnostics; + + uint8_t *pBufferCursor = buffer; + uint32_t bufferLen = nNumberOfBytesRead; + + if (TryParseString(pBufferCursor, bufferLen, dumpName) && + TryParse(pBufferCursor, bufferLen, dumpType) && + TryParse(pBufferCursor, bufferLen, diagnostics)) + { + if (!PAL_GenerateCoreDump(dumpName, dumpType, diagnostics)) + { + hr = E_FAIL; + } + } + else + { + hr = E_INVALIDARG; + } + } + else + { + hr = E_UNEXPECTED; + } + + WriteStatus(hr, pStream); + delete pStream; +} + +#endif // FEATURE_PAL +#endif // FEATURE_PERFTRACING diff --git a/src/vm/diagnosticprotocolhelper.h b/src/vm/diagnosticprotocolhelper.h new file mode 100644 index 0000000000..ca3149d878 --- /dev/null +++ b/src/vm/diagnosticprotocolhelper.h @@ -0,0 +1,28 @@ +// 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. + +#ifndef __DIAGNOSTIC_PROTOCOL_HELPER_H__ +#define __DIAGNOSTIC_PROTOCOL_HELPER_H__ + +#ifdef FEATURE_PERFTRACING + +#include "common.h" + +class IpcStream; + +class DiagnosticProtocolHelper +{ +public: + // IPC event handlers. +#ifdef FEATURE_PAL + static void GenerateCoreDump(IpcStream *pStream); // `dotnet-dump collect` +#endif + +private: + const static uint32_t IpcStreamReadBufferSize = 8192; +}; + +#endif // FEATURE_PERFTRACING + +#endif // __DIAGNOSTIC_PROTOCOL_HELPER_H__ diff --git a/src/vm/diagnosticserver.cpp b/src/vm/diagnosticserver.cpp index ba5812176b..9b9791ffec 100644 --- a/src/vm/diagnosticserver.cpp +++ b/src/vm/diagnosticserver.cpp @@ -5,6 +5,7 @@ #include "common.h" #include "diagnosticserver.h" #include "eventpipeprotocolhelper.h" +#include "diagnosticprotocolhelper.h" #ifdef FEATURE_PAL #include "pal.h" @@ -65,6 +66,12 @@ static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter) EventPipeProtocolHelper::CollectTracing(pStream); break; +#ifdef FEATURE_PAL + case DiagnosticMessageType::GenerateCoreDump: + DiagnosticProtocolHelper::GenerateCoreDump(pStream); + break; +#endif + default: STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknown request type (%d)\n", header.RequestType); delete pStream; diff --git a/src/vm/diagnosticserver.h b/src/vm/diagnosticserver.h index c3c2ca477d..49f6981247 100644 --- a/src/vm/diagnosticserver.h +++ b/src/vm/diagnosticserver.h @@ -15,12 +15,13 @@ enum class DiagnosticMessageType : uint32_t { /////////////////////////////////////////////////////////////////////////// // Debug = 0 + GenerateCoreDump = 1, // Initiates core dump generation /////////////////////////////////////////////////////////////////////////// - // EventPipe - StartEventPipeTracing = 1024, // To file + // EventPipe = 1024 + StartEventPipeTracing = 1024, // To file StopEventPipeTracing, - CollectEventPipeTracing, // To IPC + CollectEventPipeTracing, // To IPC /////////////////////////////////////////////////////////////////////////// // Profiler = 2048 |