summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/botr/xplat-minidump-generation.md2
-rw-r--r--src/debug/createdump/crashinfo.cpp10
-rw-r--r--src/pal/inc/pal.h8
-rw-r--r--src/pal/src/thread/process.cpp268
-rw-r--r--src/vm/CMakeLists.txt1
-rw-r--r--src/vm/diagnosticprotocolhelper.cpp83
-rw-r--r--src/vm/diagnosticprotocolhelper.h28
-rw-r--r--src/vm/diagnosticserver.cpp7
-rw-r--r--src/vm/diagnosticserver.h7
9 files changed, 319 insertions, 95 deletions
diff --git a/Documentation/botr/xplat-minidump-generation.md b/Documentation/botr/xplat-minidump-generation.md
index 9c56c4cf8d..3c27c52159 100644
--- a/Documentation/botr/xplat-minidump-generation.md
+++ b/Documentation/botr/xplat-minidump-generation.md
@@ -46,6 +46,8 @@ Gathering the crash information on OS X will be quite a bit different than Linux
# Configuration/Policy #
+NOTE: Core dump generation in docker containers require the ptrace capability (--cap-add=SYS_PTRACE or --privileged run/exec options).
+
Any configuration or policy is set with environment variables which are passed as options to the _createdump_ utility.
Environment variables supported:
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