crashdump.c
text/plain
Filename: crashdump.c
Type: text/plain
Part: 0
/*-------------------------------------------------------------------------
*
* crashdump.c
* Automatic crash dump creation for PostgreSQL on Windows
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#define VC_EXTRALEAN
#include <windows.h>
#include <string.h>
#include <dbghelp.h>
PG_MODULE_MAGIC;
#if !(defined(_WIN32) || defined(_WIN64))
/* Don't add lots of ifdefs here - build different files for different platforms instead,
* if adding crash dump support for additional platforms. */
#error crashdump_win32.c is only supported on MS Windows
#endif
/*
* 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/ms679294(VS.85).aspx
* http://msdn.microsoft.com/en-us/library/ms679291(VS.85).aspx
* http://msdn.microsoft.com/en-us/library/ms680519(v=VS.85).aspx
* http://msdn.microsoft.com/en-us/library/ms680360(VS.85).aspx
*/
// FIXME/TODO: elog is probably unsafe when we're crashing out. Should we just write to stderr?
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
);
/*
* To perform a crash dump, we need to load dbghelp.dll and find the
* MiniDumpWriteDump function. If possible, the copy of dbghelp.dll
* shipped with PostgreSQL is loaded; failing that, the system copy will
* be used.
*
* Called in crash handler context.
*
* If NULL is returned, loading failed and the crash dump handler should
* not try to continue.
*/
static MINIDUMPWRITEDUMP
loadDbgHelp()
{
// Use the dbghelp.dll shipped with Pg if we can find and load
// it, otherwise fall back to the copy installed in Windows.
HMODULE hDll = NULL;
MINIDUMPWRITEDUMP pDump = NULL;
char dbgHelpPath[_MAX_PATH];
elog(WARNING, "loading dll"); //XXX
if (GetModuleFileName( NULL, dbgHelpPath, _MAX_PATH ))
{
char *slash = strrchr( dbgHelpPath, '\\' );
if (slash)
{
strcpy( slash+1, "DBGHELP.DLL" );
hDll = LoadLibrary( dbgHelpPath );
}
}
elog(WARNING, "loading dll try 1, %p", (void*)hDll); //XXX
if (hDll==NULL)
{
// Load any version we can
hDll = LoadLibrary( "DBGHELP.DLL" );
}
elog(WARNING, "loading dll try 2, %p", (void*)hDll); //XXX
if (hDll==NULL)
{
elog(WARNING, "crashdump: unable to load dbghelp.dll");
}
else
{
pDump = (MINIDUMPWRITEDUMP)GetProcAddress( hDll, "MiniDumpWriteDump" );
if (pDump==NULL)
{
elog(WARNING, "crashdump: unable to get MiniDumpWriteDump from dbghelp.dll");
}
}
elog(WARNING, "pdump: %p", (void*)pDump); //XXX
return pDump;
}
/*
* 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 a
* 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 API or other functions that use PostgreSQL API.
*
*/
static LONG WINAPI
crashDumpHandler(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
MINIDUMPWRITEDUMP pDump = NULL;
char dumpPath[_MAX_PATH];
MINIDUMP_TYPE dumpType = MiniDumpNormal;
HANDLE selfProcHandle = GetCurrentProcess();
DWORD selfPid = GetProcessId(selfProcHandle);
HANDLE dumpFile;
struct _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = (BOOL)NULL;
pDump = loadDbgHelp();
if (pDump==NULL)
return EXCEPTION_CONTINUE_SEARCH;
elog(WARNING, "Generating dumppath"); //XXX
// TODO: generate a proper dump path
snprintf(&dumpPath[0], _MAX_PATH, "crashdumps\\postgres-%i.mdmp", selfPid);
dumpPath[_MAX_PATH-1] = '\0';
elog(WARNING, "Generated dumppath"); //XXX
elog(WARNING, "dumping to path: %s", &dumpPath[0]); //XXX
dumpFile = CreateFile( dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
elog(WARNING, "outfile: %p", (void*)dumpFile); //XXX
if (dumpFile==INVALID_HANDLE_VALUE)
{
elog(WARNING, "crashdump: Unable to open dump file %s for writing", &dumpPath[0]);
return EXCEPTION_CONTINUE_SEARCH;
}
elog(WARNING, "about to dump");
if( (*pDump)( selfProcHandle, selfPid, dumpFile, dumpType, &ExInfo, NULL, NULL ) )
{
elog(NOTICE,"crashdump: wrote crash dump to %s", &dumpPath[0]);
}
else
{
elog(WARNING,"crashdump: failed to write dump file");
}
CloseHandle(dumpFile);
return EXCEPTION_CONTINUE_SEARCH;
}
extern Datum crashdump_crashme(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(crashdump_crashme);
Datum
crashdump_crashme(PG_FUNCTION_ARGS)
{
int * ptr = NULL;
*ptr = 1;
return NULL;
}
void
_PG_init()
{
// http://msdn.microsoft.com/en-us/library/ms680634(VS.85).aspx
SetUnhandledExceptionFilter( crashDumpHandler );
}