diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index 0eeaa25af9a..a480a8dfd9e 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -2734,6 +2734,22 @@ cc-1020 cc: ERROR File = pqcomm.c, Line = 427 under CMD.EXE, as the MSYS console has buffering issues. + + + Collecting crash dumps on Windows + + + If PostgreSQL on Windows crashes, it has the ability to generate + minidumps that can be used to track down the cause + for the crash, similar to core dumps on Unix. These dumps can be + read using the Windows Debugger Tools or using + Visual Studio. To enable the generation of dumps + on Windows, create a subdirectory named crashdumps + inside the cluster data directory. The dumps will then be written + into this directory with a unique name based on the identifier of + the crashing process and the current time of the crash. + + diff --git a/src/backend/main/main.c b/src/backend/main/main.c index cad78d6d665..6065e8c661c 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -81,6 +81,14 @@ main(int argc, char *argv[]) */ argv = save_ps_display_args(argc, argv); + /* + * If supported on the current platform, set up a handler to be called if + * the backend/postmaster crashes with a fatal signal or exception. + */ +#ifdef WIN32 + pgwin32_install_crashdump_handler(); +#endif + /* * Set up locale information from environment. Note that LC_CTYPE and * LC_COLLATE will be overridden later from pg_control if we are in an diff --git a/src/backend/port/win32/Makefile b/src/backend/port/win32/Makefile index 8bf9f74bc46..d00c33421e7 100644 --- a/src/backend/port/win32/Makefile +++ b/src/backend/port/win32/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/port/win32 top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = timer.o socket.o signal.o security.o mingwcompat.o +OBJS = timer.o socket.o signal.o security.o mingwcompat.o crashdump.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/port/win32/crashdump.c b/src/backend/port/win32/crashdump.c new file mode 100644 index 00000000000..ba6fca75630 --- /dev/null +++ b/src/backend/port/win32/crashdump.c @@ -0,0 +1,174 @@ +/*------------------------------------------------------------------------- + * + * win32_crashdump.c + * Automatic crash dump creation for PostgreSQL on Windows + * + * The crashdump feature traps unhandled win32 exceptions produced by the + * backend, and tries to produce a Windows MiniDump crash + * dump for later debugging and analysis. The machine performing the dump + * doesn't need any special debugging tools; the user only needs to send + * the dump to somebody who has the same version of PostgreSQL and has debugging + * tools. + * + * crashdump module originally by Craig Ringer + * + * LIMITATIONS + * =========== + * This *won't* work in hard OOM situations or stack overflows. + * + * For those, it'd be necessary to take a much more complicated approach where + * the handler switches to a new stack (if it can) and forks a helper process + * to debug it self. + * + * POSSIBLE FUTURE WORK + * ==================== + * For bonus points, the crash dump format permits embedding of user-supplied + * data. If there's anything else that should always be supplied with a crash + * dump (postgresql.conf? Last few lines of a log file?), it could potentially + * be added, though at the cost of a greater chance of the crash dump failing. + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/port/win32/crashdump.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +/* + * Much of the following code is based on CodeProject and MSDN examples, + * particularly + * http://www.codeproject.com/KB/debug/postmortemdebug_standalone1.aspx + * + * Useful MSDN articles: + * + * http://msdn.microsoft.com/en-us/library/ff805116(v=VS.85).aspx + * http://msdn.microsoft.com/en-us/library/ms679294(VS.85).aspx + * + * Other useful articles on working with minidumps: + * http://www.debuginfo.com/articles/effminidumps.html + */ + +typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ); + + +/* + * This function is the exception handler passed to SetUnhandledExceptionFilter. + * It's invoked only if there's an unhandled exception. The handler will use + * dbghelp.dll to generate a crash dump, then resume the normal unhandled + * exception process, which will generally exit with an error message from + * the runtime. + * + * This function is run under the unhandled exception handler, effectively + * in a crash context, so it should be careful with memory and avoid using + * any PostgreSQL functions. + */ +static LONG WINAPI +crashDumpHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) +{ + /* + * We only write crash dumps if the "crashdumps" directory within + * the postgres data directory exists. + */ + DWORD attribs = GetFileAttributesA("crashdumps"); + if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY) ) + { + /* 'crashdumps' exists and is a directory. Try to write a dump' */ + HMODULE hDll = NULL; + MINIDUMPWRITEDUMP pDump = NULL; + MINIDUMP_TYPE dumpType; + char dumpPath[_MAX_PATH]; + HANDLE selfProcHandle = GetCurrentProcess(); + DWORD selfPid = GetProcessId(selfProcHandle); + HANDLE dumpFile; + DWORD systemTicks; + struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo; + + ExInfo.ThreadId = GetCurrentThreadId(); + ExInfo.ExceptionPointers = pExceptionInfo; + ExInfo.ClientPointers = FALSE; + + /* Load the dbghelp.dll library and functions */ + hDll = LoadLibrary("dbghelp.dll"); + if (hDll == NULL) + { + write_stderr("could not load dbghelp.dll, cannot write crashdump\n"); + return EXCEPTION_CONTINUE_SEARCH; + } + + pDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump"); + + if (pDump==NULL) + { + write_stderr("could not load required functions in dbghelp.dll, cannot write crashdump\n"); + return EXCEPTION_CONTINUE_SEARCH; + } + + /* + * Dump as much as we can, except shared memory, code segments, + * and memory mapped files. + * Exactly what we can dump depends on the version of dbghelp.dll, + * see: + * http://msdn.microsoft.com/en-us/library/ms680519(v=VS.85).aspx + */ + dumpType = MiniDumpNormal | MiniDumpWithHandleData | + MiniDumpWithDataSegs; + + if (GetProcAddress(hDll, "EnumDirTree") != NULL) + { + /* If this function exists, we have version 5.2 or newer */ + dumpType |= MiniDumpWithIndirectlyReferencedMemory | + MiniDumpWithPrivateReadWriteMemory; + } + if (GetProcAddress(hDll, "SymFromIndex") != NULL) + { + /* If this function exists, we have version 6.2 or newer */ + dumpType |= MiniDumpWithThreadInfo; + } + + systemTicks = GetTickCount(); + snprintf(dumpPath, _MAX_PATH, + "crashdumps\\postgres-pid%0i-%0i.mdmp", selfPid, systemTicks); + dumpPath[_MAX_PATH-1] = '\0'; + + dumpFile = CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL); + if (dumpFile==INVALID_HANDLE_VALUE) + { + write_stderr("could not open crash dump file %s for writing: error code %d\n", + dumpPath, GetLastError()); + return EXCEPTION_CONTINUE_SEARCH; + } + + if ((*pDump)(selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo, + NULL, NULL)) + write_stderr("wrote crash dump to %s\n", dumpPath); + else + write_stderr("could not write crash dump to %s: error code %08x\n", + dumpPath, GetLastError()); + + CloseHandle(dumpFile); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + + +void +pgwin32_install_crashdump_handler(void) +{ + SetUnhandledExceptionFilter(crashDumpHandler); +} diff --git a/src/include/port/win32.h b/src/include/port/win32.h index a9b9fcaf5b8..93439f763d0 100644 --- a/src/include/port/win32.h +++ b/src/include/port/win32.h @@ -303,6 +303,9 @@ extern int pgwin32_is_service(void); /* in backend/port/win32_shmem.c */ extern int pgwin32_ReserveSharedMemoryRegion(HANDLE); +/* in backend/port/win32/crashdump.c */ +extern void pgwin32_install_crashdump_handler(void); + /* in port/win32error.c */ extern void _dosmaperr(unsigned long);