Skip to content

Commit

Permalink
Fix some createdump issues (dotnet#40687)
Browse files Browse the repository at this point in the history
 Fix some createdump issues

Fix still generating dump with COMPlus_DbgEnableMiniDump=0 on Windows

Start adding coredump pattern to dump names.  Supported format characters:

  %%  A single % character.
  %d  PID of dumped process (for backwards createdump compatibility).
  %p  PID of dumped process.
  %P  PID of dumped process.
  %e  The process executable filename.
  %E  The process executable filename.
  %h  Hostname return by gethostname().
  %t  Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
  • Loading branch information
mikem8361 committed Aug 12, 2020
1 parent 701c5d9 commit 96d34c2
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 28 deletions.
23 changes: 17 additions & 6 deletions docs/design/coreclr/botr/xplat-minidump-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ There will be some differences gathering the crash information but these platfor

### OS X ###

As of .NET Core 5.0, createdump is supported on MacOS but instead of the MachO dump format, it generates the ELF coredumps. This is because of time constraints developing a MachO dump writer on the generation side and a MachO reader for the diagnostics tooling side (dotnet-dump and CLRMD). This means the native debuggers like gdb and lldb will not work with these dumps but the dotnet-dump tool will allow the managed state to be analyzed. Because of this behavior an additional environment variable will need to be set (COMPlus_DbgEnableElfDumpOnMacOS=1) along with the ones below in the Configuration/Policy section.
As of .NET 5.0, createdump is supported on MacOS but instead of the MachO dump format, it generates the ELF coredumps. This is because of time constraints developing a MachO dump writer on the generation side and a MachO reader for the diagnostics tooling side (dotnet-dump and CLRMD). This means the native debuggers like gdb and lldb will not work with these dumps but the dotnet-dump tool will allow the managed state to be analyzed. Because of this behavior an additional environment variable will need to be set (COMPlus_DbgEnableElfDumpOnMacOS=1) along with the ones below in the Configuration/Policy section.

### Windows ###

As of .NET Core 5.0, createdump and the below configuration environment variables are supported on Windows. It is implemented using the Windows MiniDumpWriteDump API. This allows consistent crash/unhandled exception dumps across all of our platforms.
As of .NET 5.0, createdump and the below configuration environment variables are supported on Windows. It is implemented using the Windows MiniDumpWriteDump API. This allows consistent crash/unhandled exception dumps across all of our platforms.

# Configuration/Policy #

Expand Down Expand Up @@ -79,14 +79,25 @@ The createdump utility can also be run from the command line on arbitrary .NET C

`sudo createdump <pid>`

createdump [options] pid
-f, --name - dump path and file name. The pid can be placed in the name with %d. The default is "/tmp/coredump.%d"
-n, --normal - create minidump (default).
-h, --withheap - create minidump with heap.
-f, --name - dump path and file name. The %p, %e, %h %t format characters are supported. The default is '/tmp/coredump.%p'
-n, --normal - create minidump.
-h, --withheap - create minidump with heap (default).
-t, --triage - create triage minidump.
-u, --full - create full core dump.
-d, --diag - enable diagnostic messages.


**Dump name formatting**

As of .NET 5.0, the following subset of the core pattern (see [core](https://man7.org/linux/man-pages/man5/core.5.html)) dump name formatting is supported:

%% A single % character.
%d PID of dumped process (for backwards createdump compatibility).
%p PID of dumped process.
%e The process executable filename.
%h Hostname return by gethostname().
%t Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).

# Testing #

The test plan is to modify the SOS tests in the (still) private debuggertests repo to trigger and use the core minidumps generated. Debugging managed core dumps on Linux is not supported by _mdbg_ at this time until we have a ELF core dump reader so only the SOS tests (which use _lldb_ on Linux) will be modified.
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/src/debug/createdump/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ if(CLR_CMAKE_HOST_WIN32)

set(CREATEDUMP_SOURCES
main.cpp
dumpname.cpp
createdumpwindows.cpp
createdump.rc
)
Expand All @@ -28,6 +29,7 @@ if(CLR_CMAKE_HOST_WIN32)
advapi32.lib
version.lib
dbghelp.lib
ws2_32.lib
)

else(CLR_CMAKE_HOST_WIN32)
Expand All @@ -49,6 +51,7 @@ else(CLR_CMAKE_HOST_WIN32)

set(CREATEDUMP_SOURCES
main.cpp
dumpname.cpp
createdumpunix.cpp
crashinfo.cpp
threadinfo.cpp
Expand Down
4 changes: 3 additions & 1 deletion src/coreclr/src/debug/createdump/createdump.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,6 @@ typedef int T_CONTEXT;
#define MAX_LONGPATH 1024
#endif

bool CreateDump(const char* dumpPathTemplate, int pid, MINIDUMP_TYPE minidumpType);
bool FormatDumpName(std::string& name, const char* pattern, const char* exename, int pid);
bool CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType);

15 changes: 12 additions & 3 deletions src/coreclr/src/debug/createdump/createdumpunix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
// The Linux/MacOS create dump code
//
bool
CreateDump(const char* dumpPath, int pid, MINIDUMP_TYPE minidumpType)
CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType)
{
ReleaseHolder<CrashInfo> crashInfo = new CrashInfo(pid);
DumpWriter dumpWriter(*crashInfo);
std::string dumpPath;
bool result = false;

// Initialize the crash info
if (!crashInfo->Initialize())
{
goto exit;
}
printf("Process %d %s\n", crashInfo->Pid(), crashInfo->Name().c_str());
printf("Gathering state for process %d %s\n", pid, crashInfo->Name().c_str());

// Suspend all the threads in the target process and build the list of threads
if (!crashInfo->EnumerateAndSuspendThreads())
Expand All @@ -30,7 +31,15 @@ CreateDump(const char* dumpPath, int pid, MINIDUMP_TYPE minidumpType)
{
goto exit;
}
if (!dumpWriter.OpenDump(dumpPath))
// Format the dump pattern template now that the process name on MacOS has been obtained
if (!FormatDumpName(dumpPath, dumpPathTemplate, crashInfo->Name().c_str(), pid))
{
goto exit;
}
printf("Writing %s to file %s\n", dumpType, dumpPath.c_str());

// Write the actual dump file
if (!dumpWriter.OpenDump(dumpPath.c_str()))
{
goto exit;
}
Expand Down
20 changes: 17 additions & 3 deletions src/coreclr/src/debug/createdump/createdumpwindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,42 @@
// The .NET Foundation licenses this file to you under the MIT license.

#include "createdump.h"
#include "psapi.h"

//
// The Windows create dump code
//
bool
CreateDump(const char* dumpPath, int pid, MINIDUMP_TYPE minidumpType)
CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hProcess = NULL;
bool result = false;

ArrayHolder<char> pszName = new char[MAX_LONGPATH + 1];
std::string dumpPath;

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;
}
if (GetModuleBaseNameA(hProcess, NULL, pszName, MAX_LONGPATH) <= 0)
{
fprintf(stderr, "Get process name FAILED %d\n", GetLastError());
goto exit;
}
if (!FormatDumpName(dumpPath, dumpPathTemplate, pszName, pid))
{
goto exit;
}
printf("Writing %s to file %s\n", dumpType, dumpPath.c_str());

hFile = CreateFileA(dumpPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
hFile = CreateFileA(dumpPath.c_str(), 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());
fprintf(stderr, "Invalid dump path '%s' error %d\n", dumpPath.c_str(), GetLastError());
goto exit;
}

Expand Down
124 changes: 124 additions & 0 deletions src/coreclr/src/debug/createdump/dumpname.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "createdump.h"
#include <time.h>
#ifdef HOST_WINDOWS
#include <winsock.h>
#endif

//
// Format the core dump name using a subset of the standard coredump pattern
// defined here: https://man7.org/linux/man-pages/man5/core.5.html.
//
// Supported:
//
// %% A single % character.
// %d PID of dumped process (for backwards createdump compatibility).
// %p PID of dumped process.
// %e The process executable filename.
// %h Hostname return by gethostname().
// %t Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
//
// Unsupported:
//
// %c Core file size soft resource limit of crashing process.
// %E Pathname of executable, with slashes ('/') replaced by exclamation marks ('!').
// %g Numeric real GID of dumped process.
// %i TID of thread that triggered core dump, as seen in the PID namespace in which the thread resides.
// %I TID of thread that triggered core dump, as seen in the initial PID namespace.
// %P PID of dumped process, as seen in the initial PID namespace.
// %s Number of signal causing dump.
// %u Numeric real UID of dumped process.
//
bool
FormatDumpName(std::string& name, const char* pattern, const char* exename, int pid)
{
const char* p = pattern;
if (*p == '|')
{
fprintf(stderr, "Pipe syntax in dump name not supported\n");
return false;
}

#ifdef HOST_WINDOWS
WSAData wsadata;
int wsaerr = WSAStartup(1, &wsadata);
#endif

while (*p)
{
if (*p != '%')
{
name.append(1, *p);
}
else
{
switch (*++p)
{
case '\0':
return true;

case '%':
name.append(1, '%');
break;

// process Id
case 'd':
case 'p':
name.append(std::to_string(pid));
break;

// time of dump
case 't':
time_t dumptime;
time(&dumptime);
name.append(std::to_string(dumptime));
break;

// hostname
case 'h': {
ArrayHolder<char> buffer = new char[MAX_LONGPATH + 1];
if (gethostname(buffer, MAX_LONGPATH) != 0)
{
fprintf(stderr, "Could not get the host name for dump name: %d\n",
#ifdef HOST_WINDOWS
WSAGetLastError());
#else
errno);
#endif
return false;
}
name.append(buffer);
break;
}

// executable file name
case 'e':
name.append(exename);
break;

// executable file path with / replaced with !
case 'E':
// signal number that caused the dump
case 's':
// gid
case 'g':
// coredump size limit
case 'c':
// the numeric real UID of dumped process
case 'u':
// thread id that triggered the dump
case 'i':
case 'I':
// pid of dumped process
case 'P':
default:
fprintf(stderr, "Invalid dump name format char '%c'\n", *p);
return false;
}
}
++p;
}
return true;
}
20 changes: 9 additions & 11 deletions src/coreclr/src/debug/createdump/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

#ifdef HOST_WINDOWS
#define DEFAULT_DUMP_PATH "%TEMP%\\"
#define DEFAULT_DUMP_TEMPLATE "dump.%d.dmp"
#define DEFAULT_DUMP_TEMPLATE "dump.%p.dmp"
#else
#define DEFAULT_DUMP_PATH "/tmp/"
#define DEFAULT_DUMP_TEMPLATE "coredump.%d"
#define DEFAULT_DUMP_TEMPLATE "coredump.%p"
#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 '" DEFAULT_DUMP_PATH DEFAULT_DUMP_TEMPLATE "'\n"
"-f, --name - dump path and file name. The default is '" DEFAULT_DUMP_PATH DEFAULT_DUMP_TEMPLATE "'. These specifiers are substituted with following values:\n"
" %p PID of dumped process.\n"
" %e The process executable filename.\n"
" %h Hostname return by gethostname().\n"
" %t Time of dump, expressed as seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).\n"
"-n, --normal - create minidump.\n"
"-h, --withheap - create minidump with heap (default).\n"
"-t, --triage - create triage minidump.\n"
Expand All @@ -21,8 +25,6 @@ const char* g_help = "createdump [options] pid\n"

bool g_diagnostics = false;

bool CreateDump(const char* dumpPathTemplate, int pid, MINIDUMP_TYPE minidumpType);

//
// Main entry point
//
Expand Down Expand Up @@ -116,7 +118,6 @@ 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)
{
Expand All @@ -134,18 +135,15 @@ int __cdecl main(const int argc, const char* argv[])
dumpPathTemplate = tmpPath;
}

snprintf(dumpPath, MAX_LONGPATH, dumpPathTemplate, pid);

printf("Writing %s to file %s\n", dumpType, (char*)dumpPath);

if (CreateDump(dumpPath, pid, minidumpType))
if (CreateDump(dumpPathTemplate, pid, dumpType, minidumpType))
{
printf("Dump successfully written\n");
}
else
{
exitCode = -1;
}

fflush(stdout);
fflush(stderr);
}
Expand Down
8 changes: 4 additions & 4 deletions src/coreclr/src/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4148,15 +4148,15 @@ GenerateCrashDump(
void
InitializeCrashDump()
{
bool enabled = CLRConfig::IsConfigEnabled(CLRConfig::INTERNAL_DbgEnableMiniDump);
if (enabled)
DWORD enabled = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgEnableMiniDump);
if (enabled == 1)
{
LPCWSTR dumpName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgMiniDumpName);
int dumpType = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgMiniDumpType);
bool diag = CLRConfig::IsConfigEnabled(CLRConfig::INTERNAL_CreateDumpDiagnostics);
DWORD diag = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_CreateDumpDiagnostics);

SString commandLine;
BuildCreateDumpCommandLine(commandLine, dumpName, dumpType, diag);
BuildCreateDumpCommandLine(commandLine, dumpName, dumpType, diag == 1);
g_createDumpCommandLine = commandLine.GetCopyOfUnicodeString();
}
}
Expand Down

0 comments on commit 96d34c2

Please sign in to comment.