mirror of https://github.com/postgres/postgres
Add support for collecting "minidump" style crash dumps on Windows, by setting up an exception handling filter. Crash dumps will be generated in PGDATA/crashdumps if the directory is created (the existance of the directory is used as on/off switch for the generation of the dumps). Craig Ringer and Magnus Haganderpull/1/head
parent
7e95337d58
commit
dcb09b595f
@ -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 <ringerc@ringerc.id.au> |
||||
* |
||||
* 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 <windows.h> |
||||
#include <string.h> |
||||
#include <dbghelp.h> |
||||
|
||||
/*
|
||||
* 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); |
||||
} |
Loading…
Reference in new issue